1 /*------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2019 Google Inc.
6  * Copyright (c) 2019 The Khronos Group Inc.
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  *//*!
21  * \file
22  * \brief Scissoring tests
23  *//*--------------------------------------------------------------------*/
24 
25 #include "vktDrawScissorTests.hpp"
26 
27 #include "vktDrawBaseClass.hpp"
28 #include "vkQueryUtil.hpp"
29 #include "vkCmdUtil.hpp"
30 #include "vkTypeUtil.hpp"
31 #include "vktTestGroupUtil.hpp"
32 
33 #include "tcuTestCase.hpp"
34 #include "tcuTextureUtil.hpp"
35 #include "tcuImageCompare.hpp"
36 
37 #include <string>
38 
39 namespace vkt
40 {
41 namespace Draw
42 {
43 namespace
44 {
45 using namespace vk;
46 using namespace std;
47 using namespace tcu;
48 
49 enum
50 {
51 	WIDTH = 256,
52 	HEIGHT = 256
53 };
54 
55 struct ColorQuad
56 {
ColorQuadvkt::Draw::__anon8a8f76580111::ColorQuad57 	ColorQuad	(deUint32 x, deUint32 y, deUint32 width, deUint32 height, Vec4 color)
58 	: m_x(x), m_y(y), m_width(width), m_height(height), m_color(color)
59 	{
60 	}
61 
62 	deUint32	m_x;
63 	deUint32	m_y;
64 	deUint32	m_width;
65 	deUint32	m_height;
66 	Vec4		m_color;
67 };
68 
scissorQuad(ColorQuad quad,VkRect2D scissor,VkExtent2D framebufferSize)69 ColorQuad scissorQuad (ColorQuad quad, VkRect2D scissor, VkExtent2D framebufferSize)
70 {
71 	int	left	= quad.m_x;
72 	int	right	= quad.m_x + quad.m_width;
73 	int	top		= quad.m_y;
74 	int	bottom	= quad.m_y + quad.m_height;
75 
76 	left	= de::max(left, scissor.offset.x);
77 	left	= de::max(left, 0);
78 	right	= de::min(right, scissor.offset.x + (int)scissor.extent.width);
79 	right	= de::min(right, (int)framebufferSize.width);
80 	top		= de::max(top, scissor.offset.y);
81 	top		= de::max(top, 0);
82 	bottom	= de::min(bottom, scissor.offset.y + (int)scissor.extent.height);
83 	bottom	= de::min(bottom, (int)framebufferSize.height);
84 
85 	return ColorQuad(left, top, de::max(right - left, 0), de::max(bottom - top, 0), quad.m_color);
86 }
87 
88 class TestCommand
89 {
90 	public:
TestCommand(void)91 											TestCommand		(void) {};
~TestCommand(void)92 		virtual								~TestCommand	(void) {};
93 
getVertices(deUint32 offset)94 		virtual vector<PositionColorVertex>	getVertices		(deUint32 offset) { DE_UNREF(offset); return vector<PositionColorVertex>(); }
95 		virtual void						addCommands		(const DeviceInterface& vk, VkCommandBuffer cmdBuffer) = 0;
getMaxScissor(void)96 		virtual deUint32					getMaxScissor	(void) { return 0; }
getQuad(void)97 		virtual vector<ColorQuad>			getQuad			(void) { return vector<ColorQuad>(); }
updateScissors(vector<VkRect2D> scissors)98 		virtual vector<VkRect2D>			updateScissors	(vector<VkRect2D> scissors) { return scissors; }
isScissored(void)99 		virtual bool						isScissored		(void) { return false; }
100 
101 
102 	private:
103 };
104 
105 typedef de::SharedPtr<TestCommand> TestCommandSp;
106 
107 class QuadDrawTestCommand : public TestCommand
108 {
109 	public:
110 											QuadDrawTestCommand		(deUint32 x, deUint32 y, deUint32 width, deUint32 height, Vec4 color);
~QuadDrawTestCommand(void)111 		virtual								~QuadDrawTestCommand	(void) {}
112 
113 		virtual vector<PositionColorVertex>	getVertices				(deUint32 offset);
114 		virtual void						addCommands				(const DeviceInterface& vk, VkCommandBuffer cmdBuffer);
getQuad(void)115 		virtual vector<ColorQuad>			getQuad					(void) { return vector<ColorQuad>(1, m_quad); }
isScissored(void)116 		virtual bool						isScissored				(void) { return true; }
117 	private:
118 		deUint32	m_offset;
119 		ColorQuad	m_quad;
120 };
121 
QuadDrawTestCommand(deUint32 x,deUint32 y,deUint32 width,deUint32 height,Vec4 color)122 QuadDrawTestCommand::QuadDrawTestCommand (deUint32 x, deUint32 y, deUint32 width, deUint32 height, Vec4 color)
123 : m_quad(x, y, width, height, color)
124 {
125 }
126 
getVertices(deUint32 offset)127 vector<PositionColorVertex> QuadDrawTestCommand::getVertices (deUint32 offset)
128 {
129 	vector<PositionColorVertex>	vertices;
130 	float						scaleWidth	= 2.0f / (float)WIDTH;
131 	float						scaleHeight	= 2.0f / (float)HEIGHT;
132 	Vec4						topLeft		(-1.0f + scaleWidth * (float)m_quad.m_x, -1.0f + scaleHeight * (float)m_quad.m_y, 0.0f, 1.0f);
133 	Vec4						topRight	(-1.0f + scaleWidth * (float)(m_quad.m_x + m_quad.m_width), -1.0f + scaleHeight * (float)m_quad.m_y, 0.0f, 1.0f);
134 	Vec4						bottomLeft	(-1.0f + scaleWidth * (float)m_quad.m_x, -1.0f + scaleHeight * (float)(m_quad.m_y + m_quad.m_height), 0.0f, 1.0f);
135 	Vec4						bottomRight	(-1.0f + scaleWidth * (float)(m_quad.m_x + m_quad.m_width), -1.0f + scaleHeight * (float)(m_quad.m_y + m_quad.m_height), 0.0f, 1.0f);
136 
137 	m_offset = offset;
138 
139 	vertices.push_back(PositionColorVertex(topLeft,		m_quad.m_color));
140 	vertices.push_back(PositionColorVertex(bottomRight,	m_quad.m_color));
141 	vertices.push_back(PositionColorVertex(bottomLeft,	m_quad.m_color));
142 	vertices.push_back(PositionColorVertex(topLeft,		m_quad.m_color));
143 	vertices.push_back(PositionColorVertex(topRight,	m_quad.m_color));
144 	vertices.push_back(PositionColorVertex(bottomRight,	m_quad.m_color));
145 
146 	return vertices;
147 }
148 
addCommands(const DeviceInterface & vk,VkCommandBuffer cmdBuffer)149 void QuadDrawTestCommand::addCommands (const DeviceInterface& vk, VkCommandBuffer cmdBuffer)
150 {
151 	vk.cmdDraw(cmdBuffer, 6u, 1u, m_offset, 0u);
152 }
153 
154 class RectClearTestCommand : public TestCommand
155 {
156 	public:
157 									RectClearTestCommand	(deUint32 x, deUint32 y, deUint32 width, deUint32 height, Vec4 color);
~RectClearTestCommand(void)158 		virtual						~RectClearTestCommand	(void) {}
159 
160 		virtual void				addCommands				(const DeviceInterface& vk, VkCommandBuffer cmdBuffer);
getQuad(void)161 		virtual vector<ColorQuad>	getQuad					(void) { return vector<ColorQuad>(1, m_quad); }
162 	private:
163 		ColorQuad	m_quad;
164 };
165 
RectClearTestCommand(deUint32 x,deUint32 y,deUint32 width,deUint32 height,Vec4 color)166 RectClearTestCommand::RectClearTestCommand (deUint32 x, deUint32 y, deUint32 width, deUint32 height, Vec4 color)
167 : m_quad(x, y, width, height, color)
168 {
169 }
170 
addCommands(const DeviceInterface & vk,VkCommandBuffer cmdBuffer)171 void RectClearTestCommand::addCommands (const DeviceInterface& vk, VkCommandBuffer cmdBuffer)
172 {
173 	const VkClearAttachment	attachment	=
174 	{
175 		VK_IMAGE_ASPECT_COLOR_BIT,			// VkImageAspectFlags	aspectMask
176 		0u,									// deUint32				colorAttachment
177 		makeClearValueColor(m_quad.m_color)	// VkClearValue			clearValue
178 	};
179 
180 	const VkClearRect		rect		=
181 	{
182 		makeRect2D(m_quad.m_x, m_quad.m_y, m_quad.m_width, m_quad.m_height),	// VkRect2D    rect
183 		0u,																		// deUint32    baseArrayLayer
184 		1u																		// deUint32    layerCount
185 	};
186 
187 	vk.cmdClearAttachments(cmdBuffer, 1u, &attachment, 1u, &rect);
188 }
189 
190 class DynamicScissorTestCommand : public TestCommand
191 {
192 	public:
193 									DynamicScissorTestCommand	(deUint32 firstScissor, vector<VkRect2D> scissors);
~DynamicScissorTestCommand(void)194 		virtual						~DynamicScissorTestCommand	(void) {}
195 
196 		virtual void				addCommands					(const DeviceInterface& vk, VkCommandBuffer cmdBuffer);
getMaxScissor(void)197 		virtual deUint32			getMaxScissor				(void) { return m_firstScissor + (deUint32)m_scissors.size(); }
198 		virtual vector<VkRect2D>	updateScissors				(vector<VkRect2D> scissors);
199 	private:
200 		deUint32					m_firstScissor;
201 		vector<VkRect2D>			m_scissors;
202 };
203 
DynamicScissorTestCommand(deUint32 firstScissor,vector<VkRect2D> scissors)204 DynamicScissorTestCommand::DynamicScissorTestCommand (deUint32 firstScissor, vector<VkRect2D> scissors)
205 : m_firstScissor(firstScissor)
206 , m_scissors(scissors)
207 {
208 }
209 
addCommands(const DeviceInterface & vk,VkCommandBuffer cmdBuffer)210 void DynamicScissorTestCommand::addCommands (const DeviceInterface& vk, VkCommandBuffer cmdBuffer)
211 {
212 	vk.cmdSetScissor(cmdBuffer, m_firstScissor, (deUint32)m_scissors.size(), m_scissors.data());
213 }
214 
updateScissors(vector<VkRect2D> scissors)215 vector<VkRect2D> DynamicScissorTestCommand::updateScissors (vector<VkRect2D> scissors)
216 {
217 	for (size_t scissorIdx = 0; scissorIdx < m_scissors.size(); scissorIdx++)
218 	{
219 		while (scissors.size() <= m_firstScissor + scissorIdx)
220 			scissors.push_back(makeRect2D(0, 0)); // Add dummy scissor
221 
222 		scissors[m_firstScissor + scissorIdx] = m_scissors[scissorIdx];
223 	}
224 
225 	return scissors;
226 }
227 
228 struct TestParams
229 {
TestParamsvkt::Draw::__anon8a8f76580111::TestParams230 	TestParams() : framebufferSize({WIDTH,HEIGHT}) {};
231 
232 	bool					dynamicScissor;
233 	vector<VkRect2D>		staticScissors;
234 	vector<TestCommandSp>	commands;
235 	bool					usesMultipleScissors;
236 	VkExtent2D				framebufferSize;
237 };
238 
countScissors(TestParams params)239 deUint32 countScissors (TestParams params)
240 {
241 	if (params.dynamicScissor)
242 	{
243 		deUint32 numScissors = 0u;
244 
245 		for (size_t commandIdx = 0; commandIdx < params.commands.size(); commandIdx++)
246 			numScissors = de::max(numScissors, params.commands[commandIdx]->getMaxScissor());
247 
248 		return numScissors;
249 	}
250 	else
251 		return (deUint32)params.staticScissors.size();
252 }
253 
254 class ScissorTestInstance : public TestInstance
255 {
256 public:
257 				ScissorTestInstance		(Context& context, const TestParams& params);
258 				~ScissorTestInstance	(void);
259 	TestStatus	iterate					(void);
260 private:
261 	TestParams	m_params;
262 
263 };
264 
ScissorTestInstance(Context & context,const TestParams & params)265 ScissorTestInstance::ScissorTestInstance (Context& context, const TestParams& params)
266 : vkt::TestInstance	(context)
267 , m_params			(params)
268 {
269 }
270 
~ScissorTestInstance(void)271 ScissorTestInstance::~ScissorTestInstance (void)
272 {
273 }
274 
275 class ScissorTestCase : public TestCase
276 {
277 	public:
278 							ScissorTestCase		(TestContext& context, const char* name, const char* desc, const TestParams params);
279 							~ScissorTestCase	(void);
280 	virtual	void			initPrograms		(SourceCollections& programCollection) const;
281 	virtual TestInstance*	createInstance		(Context& context) const;
282 	virtual void			checkSupport		(Context& context) const;
283 
284 private:
285 	TestParams				m_params;
286 };
287 
ScissorTestCase(TestContext & context,const char * name,const char * desc,const TestParams params)288 ScissorTestCase::ScissorTestCase (TestContext& context, const char* name, const char* desc, const TestParams params)
289 : vkt::TestCase	(context, name, desc)
290 , m_params		(params)
291 {
292 	m_params.usesMultipleScissors = params.staticScissors.size() > 1;
293 
294 	for (size_t commandIdx = 0; commandIdx < m_params.commands.size(); commandIdx++)
295 		if (m_params.commands[commandIdx]->getMaxScissor() > 1)
296 			m_params.usesMultipleScissors = true;
297 }
298 
~ScissorTestCase(void)299 ScissorTestCase::~ScissorTestCase (void)
300 {
301 }
302 
checkSupport(Context & context) const303 void ScissorTestCase::checkSupport (Context& context) const
304 {
305 	if (m_params.usesMultipleScissors)
306 	{
307 		context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_GEOMETRY_SHADER);
308 		context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_MULTI_VIEWPORT);
309 	}
310 }
311 
initPrograms(SourceCollections & programCollection) const312 void ScissorTestCase::initPrograms (SourceCollections& programCollection) const
313 {
314 	programCollection.glslSources.add("vert") << glu::VertexSource(
315 		"#version 430\n"
316 		"layout(location = 0) in vec4 in_position;\n"
317 		"layout(location = 1) in vec4 in_color;\n"
318 		"layout(location = 0) out vec4 out_color;\n"
319 		"void main()\n"
320 		"{\n"
321 		"    gl_Position  = in_position;\n"
322 		"    out_color    = in_color;\n"
323 		"}\n");
324 
325 	// Geometry shader draws the same triangles to all viewports
326 	string geomSource = string(
327 		"#version 430\n"
328 		"layout(invocations = ") + de::toString(countScissors(m_params)) + ") in;\n"
329 		"layout(triangles) in;\n"
330 		"layout(triangle_strip, max_vertices = 3) out;\n"
331 		"layout(location = 0) in vec4 in_color[];\n"
332 		"layout(location = 0) out vec4 out_color;\n"
333 		"void main()\n"
334 		"{\n"
335 		"    for (int i = 0; i < gl_in.length(); i++)\n"
336 		"    {\n"
337 		"        gl_ViewportIndex = gl_InvocationID;\n"
338 		"        gl_Position      = gl_in[i].gl_Position;\n"
339 		"        out_color        = in_color[i];\n"
340 		"        EmitVertex();\n"
341 		"    }\n"
342 		"    EndPrimitive();\n"
343 		"}\n";
344 
345 	programCollection.glslSources.add("geom") << glu::GeometrySource(geomSource);
346 
347 	programCollection.glslSources.add("frag") << glu::FragmentSource(
348 		"#version 430\n"
349 		"layout(location = 0) in vec4 in_color;\n"
350 		"layout(location = 0) out vec4 out_color;\n"
351 		"void main()\n"
352 		"{\n"
353 		"    out_color = in_color;\n"
354 		"}\n");
355 }
356 
createInstance(Context & context) const357 TestInstance* ScissorTestCase::createInstance (Context& context) const
358 {
359 	return new ScissorTestInstance(context, m_params);
360 }
361 
iterate(void)362 TestStatus ScissorTestInstance::iterate (void)
363 {
364 	ConstPixelBufferAccess			frame;
365 	de::SharedPtr<Image>			colorTargetImage;
366 	TestLog&						log						= m_context.getTestContext().getLog();
367 	const DeviceInterface&			vk						= m_context.getDeviceInterface();
368 	const VkDevice					device					= m_context.getDevice();
369 	const CmdPoolCreateInfo			cmdPoolCreateInfo		(m_context.getUniversalQueueFamilyIndex());
370 	Move<VkCommandPool>				cmdPool					= createCommandPool(vk, device, &cmdPoolCreateInfo);
371 	Move<VkCommandBuffer>			cmdBuffer				= allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
372 	const Unique<VkShaderModule>	vs						(createShaderModule(vk, device, m_context.getBinaryCollection().get("vert"), 0));
373 	Move<VkShaderModule>			gs;
374 	const Unique<VkShaderModule>	fs						(createShaderModule(vk, device, m_context.getBinaryCollection().get("frag"), 0));
375 	const deUint32					numScissors				= countScissors(m_params);
376 	VkDeviceSize					vertexBufferSize		= 0;
377 	de::SharedPtr<Buffer>			vertexBuffer;
378 	Move<VkRenderPass>				renderPass;
379 	Move<VkImageView>				colorTargetView;
380 	Move<VkFramebuffer>				framebuffer;
381 	Move<VkPipeline>				pipeline;
382 	TextureLevel					refImage;
383 	VkExtent2D						framebufferSize			= m_params.framebufferSize;
384 
385 	if (m_params.usesMultipleScissors)
386 		gs = createShaderModule(vk, device, m_context.getBinaryCollection().get("geom"), 0);
387 
388 	// Create color buffer image
389 	{
390 		const VkExtent3D		targetImageExtent		= { WIDTH, HEIGHT, 1 };
391 		const ImageCreateInfo	targetImageCreateInfo	(VK_IMAGE_TYPE_2D, VK_FORMAT_R8G8B8A8_UNORM, targetImageExtent, 1, 1, VK_SAMPLE_COUNT_1_BIT,
392 														 VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
393 		colorTargetImage = Image::createAndAlloc(vk, device, targetImageCreateInfo, m_context.getDefaultAllocator(), m_context.getUniversalQueueFamilyIndex());
394 	}
395 
396 	// Create render pass and frame buffer
397 	{
398 		const ImageViewCreateInfo	colorTargetViewInfo		(colorTargetImage->object(), VK_IMAGE_VIEW_TYPE_2D, VK_FORMAT_R8G8B8A8_UNORM);
399 		colorTargetView	= createImageView(vk, device, &colorTargetViewInfo);
400 
401 		RenderPassCreateInfo		renderPassCreateInfo;
402 		renderPassCreateInfo.addAttachment(AttachmentDescription(VK_FORMAT_R8G8B8A8_UNORM,
403 																 VK_SAMPLE_COUNT_1_BIT,
404 																 VK_ATTACHMENT_LOAD_OP_CLEAR,
405 																 VK_ATTACHMENT_STORE_OP_STORE,
406 																 VK_ATTACHMENT_LOAD_OP_DONT_CARE,
407 																 VK_ATTACHMENT_STORE_OP_STORE,
408 																 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
409 																 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL));
410 
411 		const VkAttachmentReference	colorAttachmentRef		= { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
412 		vector<VkImageView>			colorAttachment			(1, *colorTargetView);
413 		renderPassCreateInfo.addSubpass(SubpassDescription(VK_PIPELINE_BIND_POINT_GRAPHICS, 0, 0, DE_NULL, 1, &colorAttachmentRef,
414 														   DE_NULL, AttachmentReference(), 0, DE_NULL));
415 
416 		renderPass = createRenderPass(vk, device, &renderPassCreateInfo);
417 
418 		const FramebufferCreateInfo	framebufferCreateInfo(*renderPass, colorAttachment, framebufferSize.width, framebufferSize.height, 1);
419 
420 		framebuffer	= createFramebuffer(vk, device, &framebufferCreateInfo);
421 	}
422 
423 	// Create vertex buffer
424 	{
425 		vector<PositionColorVertex> vertices;
426 
427 		for (size_t commandIdx = 0; commandIdx < m_params.commands.size(); commandIdx++)
428 		{
429 			vector<PositionColorVertex> commandVertices = m_params.commands[commandIdx]->getVertices((deUint32)vertices.size());
430 			vertices.insert(vertices.end(), commandVertices.begin(), commandVertices.end());
431 		}
432 
433 		vertexBufferSize = vertices.size() * sizeof(PositionColorVertex);
434 
435 		if (vertexBufferSize > 0)
436 		{
437 			vertexBuffer = Buffer::createAndAlloc(vk, device, BufferCreateInfo(vertexBufferSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT), m_context.getDefaultAllocator(), MemoryRequirement::HostVisible);
438 			deUint8* ptr = reinterpret_cast<deUint8*>(vertexBuffer->getBoundMemory().getHostPtr());
439 
440 			deMemcpy(ptr, vertices.data(), static_cast<size_t>(vertexBufferSize));
441 			flushMappedMemoryRange(vk, device, vertexBuffer->getBoundMemory().getMemory(), vertexBuffer->getBoundMemory().getOffset(), VK_WHOLE_SIZE);
442 		}
443 	}
444 
445 	const PipelineLayoutCreateInfo	pipelineLayoutCreateInfo;
446 	Move<VkPipelineLayout>			pipelineLayout = createPipelineLayout(vk, device, &pipelineLayoutCreateInfo);
447 
448 	// Create pipeline
449 	{
450 		const PipelineCreateInfo::ColorBlendState::Attachment	colorBlendState;
451 
452 		const VkVertexInputBindingDescription					vertexInputBindingDescription	=
453 		{
454 			0,							// deUintre				binding
455 			(deUint32)sizeof(Vec4) * 2,	// deUint32				stride
456 			VK_VERTEX_INPUT_RATE_VERTEX	// VkVertexInputRate	inputRate
457 		};
458 		const VkViewport viewport = makeViewport(WIDTH, HEIGHT);
459 
460 		const VkVertexInputAttributeDescription vertexInputAttributeDescriptions[2] =
461 		{
462 			{ 0u, 0u, VK_FORMAT_R32G32B32A32_SFLOAT, 0u },
463 			{ 1u, 0u, VK_FORMAT_R32G32B32A32_SFLOAT, (deUint32)(sizeof(float) * 4) }
464 		};
465 
466 		PipelineCreateInfo::VertexInputState vertexInputState	= PipelineCreateInfo::VertexInputState(1, &vertexInputBindingDescription, 2, vertexInputAttributeDescriptions);
467 
468 		PipelineCreateInfo pipelineCreateInfo(*pipelineLayout, *renderPass, 0, 0);
469 		pipelineCreateInfo.addShader(PipelineCreateInfo::PipelineShaderStage(*vs, "main", VK_SHADER_STAGE_VERTEX_BIT));
470 		if (m_params.usesMultipleScissors)
471 			pipelineCreateInfo.addShader(PipelineCreateInfo::PipelineShaderStage(*gs, "main", VK_SHADER_STAGE_GEOMETRY_BIT));
472 		pipelineCreateInfo.addShader(PipelineCreateInfo::PipelineShaderStage(*fs, "main", VK_SHADER_STAGE_FRAGMENT_BIT));
473 		pipelineCreateInfo.addState(PipelineCreateInfo::VertexInputState(vertexInputState));
474 		pipelineCreateInfo.addState(PipelineCreateInfo::InputAssemblerState(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST));
475 		pipelineCreateInfo.addState(PipelineCreateInfo::ColorBlendState(1, &colorBlendState));
476 		pipelineCreateInfo.addState(PipelineCreateInfo::DepthStencilState());
477 		pipelineCreateInfo.addState(PipelineCreateInfo::RasterizerState());
478 		pipelineCreateInfo.addState(PipelineCreateInfo::MultiSampleState());
479 
480 		if (m_params.dynamicScissor)
481 		{
482 			pipelineCreateInfo.addState(PipelineCreateInfo::DynamicState(vector<VkDynamicState>(1, VK_DYNAMIC_STATE_SCISSOR)));
483 			pipelineCreateInfo.addState(PipelineCreateInfo::ViewportState(numScissors, vector<VkViewport>(numScissors, viewport), vector<VkRect2D>(numScissors, makeRect2D(0, 0))));
484 		}
485 		else
486 		{
487 			pipelineCreateInfo.addState(PipelineCreateInfo::ViewportState(numScissors, vector<VkViewport>(numScissors, viewport), m_params.staticScissors));
488 		}
489 
490 		pipeline = createGraphicsPipeline(vk, device, DE_NULL, &pipelineCreateInfo);
491 	}
492 
493 	// Queue commands and read results.
494 	{
495 		const ImageSubresourceRange subresourceRange	(VK_IMAGE_ASPECT_COLOR_BIT);
496 		const VkRect2D				renderArea			= makeRect2D(framebufferSize);
497 		const VkDeviceSize			vertexBufferOffset	= 0;
498 		const VkOffset3D			zeroOffset			= { 0, 0, 0 };
499 		const Vec4					clearColor			(0.0f, 0.0f, 0.0f, 1.0f);
500 
501 		clearColorImage(vk, device, m_context.getUniversalQueue(), m_context.getUniversalQueueFamilyIndex(), colorTargetImage->object(), clearColor,
502 						VK_IMAGE_LAYOUT_UNDEFINED,
503 						VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
504 						VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
505 
506 		beginCommandBuffer(vk, *cmdBuffer, 0u);
507 		beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, clearColor);
508 		if (vertexBufferSize > 0)
509 		{
510 			const VkBuffer buffer = vertexBuffer->object();
511 			vk.cmdBindVertexBuffers(*cmdBuffer, 0, 1, &buffer, &vertexBufferOffset);
512 		}
513 		vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
514 
515 		for (size_t commandIdx = 0; commandIdx < m_params.commands.size(); commandIdx++)
516 			m_params.commands[commandIdx]->addCommands(vk, *cmdBuffer);
517 
518 		endRenderPass(vk, *cmdBuffer);
519 		transition2DImage(vk, *cmdBuffer, colorTargetImage->object(),
520 						  VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
521 						  VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
522 						  VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
523 						  VK_PIPELINE_STAGE_TRANSFER_BIT);
524 		endCommandBuffer(vk, *cmdBuffer);
525 		submitCommandsAndWait(vk, device, m_context.getUniversalQueue(), cmdBuffer.get());
526 
527 		frame = colorTargetImage->readSurface(m_context.getUniversalQueue(), m_context.getDefaultAllocator(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, zeroOffset, WIDTH, HEIGHT, VK_IMAGE_ASPECT_COLOR_BIT);
528 	}
529 
530 	// Generate reference
531 	{
532 		refImage.setStorage(vk::mapVkFormat(VK_FORMAT_R8G8B8A8_UNORM), WIDTH, HEIGHT);
533 		clear(refImage.getAccess(), Vec4(0.0f, 0.0f, 0.0f, 1.0f));
534 
535 		vector<VkRect2D> scissors = m_params.staticScissors;
536 
537 		for (size_t commandIdx = 0; commandIdx < m_params.commands.size(); commandIdx++)
538 		{
539 			scissors = m_params.commands[commandIdx]->updateScissors(scissors);
540 
541 			vector<ColorQuad> quad = m_params.commands[commandIdx]->getQuad();
542 
543 			if (quad.empty())
544 				continue;
545 
546 			for (size_t scissorIdx = 0; scissorIdx < scissors.size(); scissorIdx++)
547 			{
548 				ColorQuad scissoredQuad = m_params.commands[commandIdx]->isScissored() ? scissorQuad(quad[0], scissors[scissorIdx], framebufferSize) : quad[0];
549 
550 				if (scissoredQuad.m_width == 0 || scissoredQuad.m_height == 0)
551 					continue;
552 
553 				clear(getSubregion(refImage.getAccess(), scissoredQuad.m_x, scissoredQuad.m_y, 0, scissoredQuad.m_width, scissoredQuad.m_height, 1), scissoredQuad.m_color);
554 			}
555 		}
556 	}
557 
558 	// Compare results
559 	qpTestResult res = QP_TEST_RESULT_PASS;
560 
561 	if (!intThresholdCompare(log, "Result", "Image comparison result", refImage.getAccess(), frame, UVec4(0), COMPARE_LOG_RESULT))
562 		res = QP_TEST_RESULT_FAIL;
563 
564 	return TestStatus(res, qpGetTestResultName(res));
565 }
566 
createTests(TestCaseGroup * testGroup)567 void createTests (TestCaseGroup* testGroup)
568 {
569 	TestContext&		testCtx		= testGroup->getTestContext();
570 	const Vec4			red			(1.0f, 0.0f, 0.0f, 1.0f);
571 	const Vec4			green		(0.0f, 1.0f, 0.0f, 1.0f);
572 	const Vec4			blue		(0.0f, 0.0f, 1.0f, 1.0f);
573 	const Vec4			yellow		(1.0f, 1.0f, 0.0f, 1.0f);
574 
575 	// Two quads with a single static scissor
576 	{
577 		TestParams params;
578 		params.dynamicScissor = false;
579 		params.staticScissors.push_back(makeRect2D(30, 40, WIDTH - 60, HEIGHT - 80));
580 		params.commands.push_back(TestCommandSp(new QuadDrawTestCommand(10, 10, 50, 50, red)));
581 		params.commands.push_back(TestCommandSp(new QuadDrawTestCommand(WIDTH - 80, HEIGHT - 100, 30, 40, green)));
582 
583 		testGroup->addChild(new ScissorTestCase(testCtx, "static_scissor_two_quads", "", params));
584 	}
585 
586 	// Two clears with a single static scissor
587 	{
588 		TestParams params;
589 		params.dynamicScissor = false;
590 		params.staticScissors.push_back(makeRect2D(30, 40, WIDTH - 60, HEIGHT - 80));
591 		params.commands.push_back(TestCommandSp(new RectClearTestCommand(10, 10, 50, 50, red)));
592 		params.commands.push_back(TestCommandSp(new RectClearTestCommand(WIDTH - 80, HEIGHT - 100, 30, 40, green)));
593 
594 		testGroup->addChild(new ScissorTestCase(testCtx, "static_scissor_two_clears", "", params));
595 	}
596 
597 	// One quad with two static scissors
598 	{
599 		TestParams params;
600 		params.dynamicScissor = false;
601 		params.staticScissors.push_back(makeRect2D(30, 40, WIDTH - 60, HEIGHT - 70));
602 		params.staticScissors.push_back(makeRect2D(40, 50, WIDTH - 60, HEIGHT - 70));
603 		params.commands.push_back(TestCommandSp(new QuadDrawTestCommand(10, 10, WIDTH - 10, HEIGHT - 10, red)));
604 
605 		testGroup->addChild(new ScissorTestCase(testCtx, "two_static_scissors_one_quad", "", params));
606 	}
607 
608 	// Static scissor extending outside viewport
609 	{
610 		TestParams params;
611 		params.dynamicScissor = false;
612 		params.staticScissors.push_back(makeRect2D(30, 40, WIDTH, HEIGHT));
613 		params.commands.push_back(TestCommandSp(new QuadDrawTestCommand(0, 0, WIDTH, HEIGHT + 30, green)));
614 
615 		testGroup->addChild(new ScissorTestCase(testCtx, "static_scissor_partially_outside_viewport", "", params));
616 	}
617 
618 	// Static scissor completely outside viewport
619 	{
620 		TestParams params;
621 		params.dynamicScissor = false;
622 		params.staticScissors.push_back(makeRect2D(WIDTH + 30, HEIGHT + 40, WIDTH, HEIGHT));
623 		params.commands.push_back(TestCommandSp(new QuadDrawTestCommand(100, 100, 20, 30, green)));
624 
625 		testGroup->addChild(new ScissorTestCase(testCtx, "static_scissor_outside_viewport", "", params));
626 	}
627 
628 	// Static scissor outside viewport and touching right border of viewport
629 	{
630 		TestParams params;
631 		params.dynamicScissor = false;
632 		params.staticScissors.push_back(makeRect2D(WIDTH, 0, WIDTH, HEIGHT));
633 		params.commands.push_back(TestCommandSp(new QuadDrawTestCommand(100, 100, 20, 30, green)));
634 
635 		testGroup->addChild(new ScissorTestCase(testCtx, "static_scissor_viewport_border", "", params));
636 	}
637 
638 	// Static scissor with offset + extent equal to largest positive int32
639 	{
640 		TestParams params;
641 		params.dynamicScissor = false;
642 		params.staticScissors.push_back(makeRect2D(100, 100, 0x7fffffff - 100, 0x7fffffff - 100));
643 		params.commands.push_back(TestCommandSp(new QuadDrawTestCommand(0, 0, WIDTH, HEIGHT, green)));
644 
645 		testGroup->addChild(new ScissorTestCase(testCtx, "static_scissor_max_int32", "", params));
646 	}
647 
648 	// 16 static scissors (minimum number required when multiViewport supported)
649 	{
650 		TestParams params;
651 		params.dynamicScissor = false;
652 
653 		for (deUint32 i = 0; i < 16; i++)
654 			params.staticScissors.push_back(makeRect2D(10 + i * 3, 20 + i * 2, WIDTH / 2, HEIGHT / 2));
655 
656 		params.commands.push_back(TestCommandSp(new QuadDrawTestCommand(5, 6, WIDTH - 10, HEIGHT - 2, red)));
657 
658 		testGroup->addChild(new ScissorTestCase(testCtx, "16_static_scissors", "", params));
659 	}
660 
661 	// Two quads with an empty scissor
662 	{
663 		TestParams params;
664 		params.dynamicScissor = false;
665 		params.staticScissors.push_back(makeRect2D(0, 0, 0, 0));
666 		params.commands.push_back(TestCommandSp(new QuadDrawTestCommand(10, 10, 50, 50, red)));
667 		params.commands.push_back(TestCommandSp(new QuadDrawTestCommand(WIDTH - 80, HEIGHT - 100, 30, 40, green)));
668 
669 		testGroup->addChild(new ScissorTestCase(testCtx, "empty_static_scissor", "", params));
670 	}
671 
672 	// Two quads with a single dynamic scissor
673 	{
674 		TestParams params;
675 		params.dynamicScissor = true;
676 		params.commands.push_back(TestCommandSp(new DynamicScissorTestCommand(0, vector<VkRect2D>(1, makeRect2D(30, 40, WIDTH - 60, HEIGHT - 80)))));
677 		params.commands.push_back(TestCommandSp(new QuadDrawTestCommand(10, 10, 50, 50, red)));
678 		params.commands.push_back(TestCommandSp(new QuadDrawTestCommand(WIDTH - 80, HEIGHT - 100, 30, 40, green)));
679 
680 		testGroup->addChild(new ScissorTestCase(testCtx, "dynamic_scissor_two_quads", "", params));
681 	}
682 
683 	// Empty scissor for the first draw
684 	{
685 		TestParams params;
686 		params.dynamicScissor = true;
687 		params.commands.push_back(TestCommandSp(new DynamicScissorTestCommand(0, vector<VkRect2D>(1, makeRect2D(0, 0, 0, 0)))));
688 		params.commands.push_back(TestCommandSp(new QuadDrawTestCommand(10, 10, 50, 50, red)));
689 		params.commands.push_back(TestCommandSp(new DynamicScissorTestCommand(0, vector<VkRect2D>(1, makeRect2D(30, 40, WIDTH - 60, HEIGHT - 80)))));
690 		params.commands.push_back(TestCommandSp(new QuadDrawTestCommand(WIDTH - 80, HEIGHT - 100, 30, 40, green)));
691 
692 		testGroup->addChild(new ScissorTestCase(testCtx, "empty_dynamic_scissor_first_draw", "", params));
693 	}
694 
695 	// Two quads with three scissors updated in between
696 	{
697 		TestParams			params;
698 		VkRect2D			rect		= makeRect2D(10, 20, WIDTH - 60, HEIGHT - 70);
699 		vector<VkRect2D>	scissors;
700 
701 		params.dynamicScissor = true;
702 		scissors.push_back(rect);
703 		rect.offset.x += 10;
704 		rect.offset.y += 10;
705 		scissors.push_back(rect);
706 		rect.offset.x += 10;
707 		rect.offset.y += 10;
708 		scissors.push_back(rect);
709 
710 		params.commands.push_back(TestCommandSp(new DynamicScissorTestCommand(0, scissors)));
711 		params.commands.push_back(TestCommandSp(new QuadDrawTestCommand(5, 7, WIDTH - 20, HEIGHT - 9, red)));
712 
713 		for (size_t scissorIdx = 0; scissorIdx < scissors.size(); scissorIdx++)
714 			scissors[scissorIdx].offset.x += 20;
715 
716 		params.commands.push_back(TestCommandSp(new DynamicScissorTestCommand(0, scissors)));
717 		params.commands.push_back(TestCommandSp(new QuadDrawTestCommand(8, 12, WIDTH - 2, HEIGHT - 19, green)));
718 
719 		testGroup->addChild(new ScissorTestCase(testCtx, "dynamic_scissor_updates_between_draws", "", params));
720 	}
721 
722 	// Scissor updates out of order
723 	{
724 		TestParams			params;
725 		VkRect2D			rect		= makeRect2D(10, 20, WIDTH - 60, HEIGHT - 70);
726 		vector<VkRect2D>	scissors;
727 
728 		params.dynamicScissor = true;
729 		scissors.push_back(rect);
730 		rect.offset.x += 10;
731 		rect.offset.y += 10;
732 		scissors.push_back(rect);
733 		rect.offset.x += 10;
734 		rect.offset.y += 10;
735 		scissors.push_back(rect);
736 
737 		params.commands.push_back(TestCommandSp(new DynamicScissorTestCommand(2, vector<VkRect2D>(1, scissors[2]))));
738 		params.commands.push_back(TestCommandSp(new DynamicScissorTestCommand(1, vector<VkRect2D>(1, scissors[1]))));
739 		params.commands.push_back(TestCommandSp(new DynamicScissorTestCommand(0, vector<VkRect2D>(1, scissors[0]))));
740 		params.commands.push_back(TestCommandSp(new QuadDrawTestCommand(5, 7, WIDTH - 20, HEIGHT - 9, red)));
741 
742 		for (size_t scissorIdx = 0; scissorIdx < scissors.size(); scissorIdx++)
743 			scissors[scissorIdx].offset.x += 20;
744 
745 		params.commands.push_back(TestCommandSp(new DynamicScissorTestCommand(1, vector<VkRect2D>(1, scissors[1]))));
746 		params.commands.push_back(TestCommandSp(new DynamicScissorTestCommand(0, vector<VkRect2D>(1, scissors[0]))));
747 		params.commands.push_back(TestCommandSp(new DynamicScissorTestCommand(2, vector<VkRect2D>(1, scissors[2]))));
748 		params.commands.push_back(TestCommandSp(new QuadDrawTestCommand(8, 12, WIDTH - 2, HEIGHT - 19, green)));
749 
750 		testGroup->addChild(new ScissorTestCase(testCtx, "dynamic_scissor_out_of_order_updates", "", params));
751 	}
752 
753 	// Dynamic scissor extending outside viewport
754 	{
755 		TestParams params;
756 		params.dynamicScissor = true;
757 		params.commands.push_back(TestCommandSp(new DynamicScissorTestCommand(0, vector<VkRect2D>(1, makeRect2D(30, 40, WIDTH, HEIGHT)))));
758 		params.commands.push_back(TestCommandSp(new QuadDrawTestCommand(0, 0, WIDTH + 50, HEIGHT + 20, green)));
759 
760 		testGroup->addChild(new ScissorTestCase(testCtx, "dynamic_scissor_partially_outside_viewport", "", params));
761 	}
762 
763 	// Dynamic scissor completely outside viewport
764 	{
765 		TestParams params;
766 		params.dynamicScissor = true;
767 		params.commands.push_back(TestCommandSp(new DynamicScissorTestCommand(0, vector<VkRect2D>(1, makeRect2D(WIDTH + 30, HEIGHT + 40, WIDTH, HEIGHT)))));
768 		params.commands.push_back(TestCommandSp(new QuadDrawTestCommand(100, 100, 20, 30, green)));
769 
770 		testGroup->addChild(new ScissorTestCase(testCtx, "dynamic_scissor_outside_viewport", "", params));
771 	}
772 
773 	// Dynamic scissor outside viewport and touching right border of viewport
774 	{
775 		TestParams params;
776 		params.dynamicScissor = true;
777 		params.commands.push_back(TestCommandSp(new DynamicScissorTestCommand(0, vector<VkRect2D>(1, makeRect2D(WIDTH, 0, WIDTH, HEIGHT)))));
778 		params.commands.push_back(TestCommandSp(new QuadDrawTestCommand(100, 100, 20, 30, green)));
779 
780 		testGroup->addChild(new ScissorTestCase(testCtx, "dynamic_scissor_viewport_border", "", params));
781 	}
782 
783 	// Dynamic scissor with offset + extent equal to largest positive int32
784 	{
785 		TestParams params;
786 		params.dynamicScissor = true;
787 		params.commands.push_back(TestCommandSp(new DynamicScissorTestCommand(0, vector<VkRect2D>(1, makeRect2D(100, 100, 0x7fffffff - 100, 0x7fffffff - 100)))));
788 		params.commands.push_back(TestCommandSp(new QuadDrawTestCommand(0, 0, WIDTH, HEIGHT, green)));
789 
790 		testGroup->addChild(new ScissorTestCase(testCtx, "dynamic_scissor_max_int32", "", params));
791 	}
792 
793 	// 16 dynamic scissors (minimum number required when multiViewport supported)
794 	{
795 		TestParams			params;
796 		vector<VkRect2D>	scissors;
797 		params.dynamicScissor = true;
798 
799 		for (deUint32 i = 0; i < 16; i++)
800 			scissors.push_back(makeRect2D(10 + i * 3, 20 + i * 2, WIDTH / 2, HEIGHT / 2));
801 
802 		params.commands.push_back(TestCommandSp(new DynamicScissorTestCommand(0, scissors)));
803 		params.commands.push_back(TestCommandSp(new QuadDrawTestCommand(5, 6, WIDTH - 10, HEIGHT - 2, red)));
804 
805 		testGroup->addChild(new ScissorTestCase(testCtx, "16_dynamic_scissors", "", params));
806 	}
807 
808 	// Two clears with a single dynamic scissor
809 	{
810 		TestParams params;
811 		params.dynamicScissor = true;
812 		params.commands.push_back(TestCommandSp(new DynamicScissorTestCommand(0, vector<VkRect2D>(1, makeRect2D(30, 40, WIDTH - 60, HEIGHT - 80)))));
813 		params.commands.push_back(TestCommandSp(new RectClearTestCommand(10, 10, 50, 50, red)));
814 		params.commands.push_back(TestCommandSp(new RectClearTestCommand(WIDTH - 80, HEIGHT - 100, 30, 40, green)));
815 
816 		testGroup->addChild(new ScissorTestCase(testCtx, "dynamic_scissor_two_clears", "", params));
817 	}
818 
819 	// Mixture of quad draws and clears with dynamic scissor updates
820 	{
821 		TestParams			params;
822 		vector<VkRect2D>	scissors;
823 
824 		params.dynamicScissor = true;
825 		scissors.push_back(makeRect2D(30, 40, 50, 60));
826 		scissors.push_back(makeRect2D(40, 20, 50, 50));
827 		params.commands.push_back(TestCommandSp(new DynamicScissorTestCommand(0, scissors)));
828 		params.commands.push_back(TestCommandSp(new RectClearTestCommand(10, 10, 50, 50, red)));
829 		params.commands.push_back(TestCommandSp(new QuadDrawTestCommand(40, 30, 50, 50, green)));
830 		scissors[1].extent.width -= 20;
831 		scissors[1].extent.height += 30;
832 		scissors[1].offset.x -= 20;
833 		params.commands.push_back(TestCommandSp(new DynamicScissorTestCommand(1, vector<VkRect2D>(1, scissors[1]))));
834 		params.commands.push_back(TestCommandSp(new QuadDrawTestCommand(70, 70, 50, 50, blue)));
835 		params.commands.push_back(TestCommandSp(new RectClearTestCommand(75, 77, 50, 50, yellow)));
836 
837 		testGroup->addChild(new ScissorTestCase(testCtx, "dynamic_scissor_mix", "", params));
838 	}
839 
840 	// Static scissor off by one, inside frame buffer border
841 	{
842 		VkExtent2D size =
843 		{
844 			WIDTH / 2 - 1,
845 			HEIGHT / 2 - 1
846 		};
847 
848 		TestParams params;
849 
850 		params.framebufferSize = size;
851 		params.dynamicScissor = false;
852 		params.staticScissors.push_back(makeRect2D(1, 1, size.width - 2, size.height - 2));
853 		params.commands.push_back(TestCommandSp(new QuadDrawTestCommand(0, 0, WIDTH * 4, HEIGHT * 4, red)));
854 
855 		testGroup->addChild(new ScissorTestCase(testCtx, "static_scissor_framebuffer_border_in", "", params));
856 	}
857 
858 	// Dynamic scissor off by one, inside frame buffer border
859 	{
860 		VkExtent2D size =
861 		{
862 			WIDTH / 2 - 1,
863 			HEIGHT / 2 - 1
864 		};
865 
866 		TestParams			params;
867 		vector<VkRect2D>	scissors;
868 
869 		params.framebufferSize = size;
870 		params.dynamicScissor = true;
871 
872 		scissors.push_back(makeRect2D(1, 1, size.width - 2, size.height - 2));
873 		params.commands.push_back(TestCommandSp(new DynamicScissorTestCommand(0, scissors)));
874 		params.commands.push_back(TestCommandSp(new QuadDrawTestCommand(0, 0, WIDTH * 4, HEIGHT * 4, red)));
875 
876 		testGroup->addChild(new ScissorTestCase(testCtx, "dynamic_scissor_framebuffer_border_in", "", params));
877 	}
878 
879 	// Static scissor off by one, outside frame buffer border
880 	{
881 		VkExtent2D size =
882 		{
883 			WIDTH / 2 - 1,
884 			HEIGHT / 2 - 1
885 		};
886 
887 		TestParams params;
888 
889 		params.framebufferSize = size;
890 		params.dynamicScissor = false;
891 
892 		params.staticScissors.push_back(makeRect2D(0, 0, size.width + 1, size.height + 1));
893 		params.commands.push_back(TestCommandSp(new QuadDrawTestCommand(0, 0, WIDTH * 4, HEIGHT * 4, red)));
894 
895 		testGroup->addChild(new ScissorTestCase(testCtx, "static_scissor_framebuffer_border_out", "", params));
896 	}
897 
898 	// Dynamic scissor off by one, outside frame buffer border
899 	{
900 		VkExtent2D size =
901 		{
902 			WIDTH / 2 - 1,
903 			HEIGHT / 2 - 1
904 		};
905 
906 		TestParams			params;
907 		vector<VkRect2D>	scissors;
908 
909 		params.framebufferSize = size;
910 		params.dynamicScissor = true;
911 
912 		scissors.push_back(makeRect2D(0, 0, size.width + 1, size.height + 1));
913 		params.commands.push_back(TestCommandSp(new DynamicScissorTestCommand(0, scissors)));
914 		params.commands.push_back(TestCommandSp(new QuadDrawTestCommand(0, 0, WIDTH * 4, HEIGHT * 4, red)));
915 
916 		testGroup->addChild(new ScissorTestCase(testCtx, "dynamic_scissor_framebuffer_border_out", "", params));
917 	}
918 
919 	// Static oversized scissor, exceeds frame buffer and image attachment sizes
920 	{
921 		VkExtent2D size =
922 		{
923 			WIDTH / 2 - 1,
924 			HEIGHT / 2 - 1
925 		};
926 
927 		TestParams params;
928 
929 		params.framebufferSize = size;
930 		params.dynamicScissor = false;
931 
932 		params.staticScissors.push_back(makeRect2D(0, 0, WIDTH * 2, HEIGHT * 2));
933 		params.commands.push_back(TestCommandSp(new QuadDrawTestCommand(0, 0, WIDTH * 4, HEIGHT * 4, red)));
934 
935 		testGroup->addChild(new ScissorTestCase(testCtx, "static_scissor_oversized", "", params));
936 	}
937 
938 	// Dynamic oversized scissor, exceeds frame buffer and image attachment sizes
939 	{
940 		VkExtent2D size =
941 		{
942 			WIDTH / 2 - 1,
943 			HEIGHT / 2 - 1
944 		};
945 
946 		TestParams			params;
947 		vector<VkRect2D>	scissors;
948 
949 		params.framebufferSize = size;
950 		params.dynamicScissor = true;
951 
952 		scissors.push_back(makeRect2D(0, 0, WIDTH * 2, HEIGHT * 2));
953 		params.commands.push_back(TestCommandSp(new DynamicScissorTestCommand(0, scissors)));
954 		params.commands.push_back(TestCommandSp(new QuadDrawTestCommand(0, 0, WIDTH * 4, HEIGHT * 4, red)));
955 
956 		testGroup->addChild(new ScissorTestCase(testCtx, "dynamic_scissor_oversized", "", params));
957 	}
958 }
959 
960 }	// anonymous
961 
createScissorTests(TestContext & testCtx)962 TestCaseGroup*	createScissorTests (TestContext& testCtx)
963 {
964 	return createTestGroup(testCtx, "scissor", "Scissor tests", createTests);
965 }
966 
967 }	// Draw
968 }	// vkt
969