• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2020 NVIDIA Corporation
6  * Copyright (c) 2023 LunarG, Inc.
7  * Copyright (c) 2023 Nintendo
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  *
21  *//*!
22  * \file
23  * \brief VK_NV_inherited_viewport_scissor Tests
24  *
25  * Simple test cases for secondary command buffers inheriting dynamic
26  * viewport and scissor state from the calling primary command buffer
27  * or an earlier secondary command buffer. Tests draw a bunch of color
28  * rectangles using a trivial geometry pipeline (no vertex
29  * transformation except for fixed-function viewport transform,
30  * geometry shader selects viewport/scissor index). The depth test is
31  * enabled to check for incorrect depth transformation.
32  *//*--------------------------------------------------------------------*/
33 #include "vktDynamicStateInheritanceTests.hpp"
34 
35 #include <math.h>
36 #include <sstream>
37 #include <vector>
38 
39 #include "vkBufferWithMemory.hpp"
40 #include "vkImageWithMemory.hpp"
41 
42 using namespace vk;
43 using tcu::Vec2;
44 using tcu::Vec3;
45 
46 namespace vkt
47 {
48 namespace DynamicState
49 {
50 namespace
51 {
52 // Size of test framebuffer, power of 2 to avoid rounding errors.
53 static const deInt32 kWidth = 256, kHeight = 128;
54 
55 // Maximum viewport/scissors, and maximum rectangles, for any test case.
56 static const deUint32 kMaxViewports = 16, kMaxRectangles = 1024;
57 
58 // Color format of framebuffer image, this seems universally supported.
59 static const VkFormat kFormat = VK_FORMAT_B8G8R8A8_UNORM;
60 
61 // Texel data matching kFormat, and functions for converting to/from
62 // packed 32-bit color. alpha is unused.
63 struct Texel
64 {
65 	deUint8 blue, green, red, alpha;
66 };
67 
texelFrom_r8g8b8(deInt32 r8g8b8)68 inline Texel texelFrom_r8g8b8(deInt32 r8g8b8)
69 {
70 	return {deUint8(r8g8b8 & 255),
71 			deUint8((r8g8b8 >> 8) & 255),
72 			deUint8((r8g8b8 >> 16) & 255), 0u};
73 }
74 
75 // Parameters of axis-aligned rectangle to rasterize.  No mvp matrix
76 // or anything, only testing fixed-function viewport transformation.
77 struct Rectangle
78 {
79 	Vec3    xyz;         // Before viewport transformation
80 	deInt32 r8g8b8;      // (8-bit) red << 16 | green << 8 | blue
81 	Vec2    widthHeight; // positive; before viewport transformation
82 	deInt32 viewportIndex;
83 };
84 
85 // Determines where the secondary command buffer's inherited viewport/scissor state comes from (if inherited at all).
86 enum InheritanceMode
87 {
88 	kInheritanceDisabled,   // Disable extension, use non-dynamic viewport/scissor count
89 	kInheritFromPrimary,    // Inherit from calling primary cmd buffer
90 	kInheritFromSecondary,  // Inherit from earlier secondary cmd buffer
91 	kSplitInheritance,      // Split viewport/scissor array in two, inherit
92 							// some from primary and rest from secondary
93 
94 	// Inherit state-with-count-EXT from calling primary cmd buffer
95 	kInheritFromPrimaryWithCount,
96 	// Inherit state-with-count-EXT from earlier secondary cmd buffer
97 	kInheritFromSecondaryWithCount,
98 };
99 
100 // Input test geometry.
101 struct TestGeometry
102 {
103 	// Color and depth to clear the framebuffer to.
104 	Vec3    clearColor;
105 	float   clearDepth;
106 
107 	// List of rectangles to rasterize, in order.
108 	std::vector<Rectangle>  rectangles;
109 
110 	// List of viewports and scissors to use, both vectors must have
111 	// same length and have length at least 1.
112 	std::vector<VkViewport> viewports;
113 	std::vector<VkRect2D>   scissors;
114 	InheritanceMode inheritanceMode;
115 };
116 
117 
118 // Whether the test was a success, and both the device-rasterized image
119 // and the CPU-computed expected image.
120 struct TestResults
121 {
122 	bool passed;
123 
124 	// Index with [y][x]
125 	Texel   deviceResult[kHeight][kWidth];
126 	Texel expectedResult[kHeight][kWidth];
127 };
128 
129 
130 class InheritanceTestInstance : public TestInstance
131 {
132 	const vk::InstanceInterface& m_in;
133 	const vk::DeviceInterface&   m_vk;
134 	InheritanceMode              m_inheritanceMode;
135 
136 	PipelineConstructionType	 m_pipelineConstructionType;
137 
138 	// Vertex buffer storing rectangle list, and its mapping and
139 	// backing memory. kMaxRectangles is its capacity (in Rectangles).
140 	BufferWithMemory m_rectangleBuffer;
141 
142 	// Buffer for downloading rendered image from device
143 	BufferWithMemory m_downloadBuffer;
144 
145 	// Image attachments and views.
146 	// Create info for depth buffer set at runtime due to depth format search.
147 	VkImageCreateInfo     m_depthImageInfo;
148 	ImageWithMemory       m_colorImage,    m_depthImage;
149 	VkImageViewCreateInfo m_colorViewInfo, m_depthViewInfo;
150 	Unique<VkImageView>   m_colorView,     m_depthView;
151 
152 	// Simple render pass and framebuffer.
153 	RenderPassWrapper	  m_renderPass;
154 
155 	// Shader modules for graphics pipelines.
156 	ShaderWrapper		 m_vertModule, m_geomModule, m_fragModule;
157 
158 	// Geometry shader pipeline, converts points into rasterized
159 	// struct Rectangles using geometry shader, which also selects the
160 	// viewport to use. Pipeline array maps viewport/scissor count to
161 	// the pipeline to use (special value 0 indicates that
162 	// viewport/scissor count is dynamic state).
163 	PipelineLayoutWrapper					m_rectanglePipelineLayout;
164 	std::vector<GraphicsPipelineWrapper>	m_rectanglePipelines;
165 
166 	// Command pool
167 	Move<VkCommandPool> m_cmdPool;
168 
169 	// Primary command buffer, re-used for every test
170 	Move<VkCommandBuffer> m_primaryCmdBuffer;
171 
172 	// Secondary command buffers, first for specifying
173 	// viewport/scissor state, second for subpass contents.
174 	// Both re-used to check for stale state.
175 	Move<VkCommandBuffer> m_setStateCmdBuffer, m_subpassCmdBuffer;
176 
177 	// "depth buffer" used for CPU rasterization of expected image.
178 	float m_cpuDepthBuffer[kHeight][kWidth];
179 
180 public:
181 	InheritanceTestInstance(Context& context, PipelineConstructionType pipelineConstructionType, InheritanceMode inheritanceMode);
182 	tcu::TestStatus iterate(void);
183 
184 private:
185 	void startRenderCmds(const TestGeometry& geometry);
186 	void rasterizeExpectedResults(const TestGeometry& geometry, Texel (&output)[kHeight][kWidth]);
187 };
188 
189 
190 
191 // Most state for graphics pipeline
192 namespace pipelinestate {
193 
194 // Vertex shader, just pass through Rectangle data.
195 const char vert_glsl[] =
196 "#version 460\n"
197 "\n"
198 "layout(location=0) in vec3 xyz;\n"
199 "layout(location=1) in int r8g8b8;\n"
200 "layout(location=2) in vec2 widthHeight;\n"
201 "layout(location=3) in int viewportIndex;\n"
202 "\n"
203 "layout(location=0) flat out int o_r8g8b8;\n"
204 "layout(location=1) flat out vec2 o_widthHeight;\n"
205 "layout(location=2) flat out int o_viewportIndex;\n"
206 "\n"
207 "void main()\n"
208 "{\n"
209 "	gl_Position     = vec4(xyz, 1.0);\n"
210 "	o_r8g8b8        = r8g8b8;\n"
211 "	o_widthHeight   = widthHeight;\n"
212 "	o_viewportIndex = viewportIndex;\n"
213 "}\n";
214 
215 // Geometry shader, convert points to rectangles and select correct viewport.
216 const char geom_glsl[] =
217 "#version 460\n"
218 "\n"
219 "layout(points) in;\n"
220 "layout(triangle_strip, max_vertices=4) out;\n"
221 "\n"
222 "layout(location=0) flat in int r8g8b8[];\n"
223 "layout(location=1) flat in vec2 widthHeight[];\n"
224 "layout(location=2) flat in int viewportIndex[];\n"
225 "\n"
226 "layout(location=0) flat out vec4 o_color;\n"
227 "\n"
228 "void main()\n"
229 "{\n"
230 "	int redBits   = (r8g8b8[0] >> 16) & 255;\n"
231 "	int greenBits = (r8g8b8[0] >> 8)  & 255;\n"
232 "	int blueBits  =  r8g8b8[0]        & 255;\n"
233 "	float n       = 1.0 / 255.0;\n"
234 "	vec4 color    = vec4(redBits * n, greenBits * n, blueBits * n, 1.0);\n"
235 "\n"
236 "	gl_ViewportIndex = viewportIndex[0];\n"
237 "	gl_Position = gl_in[0].gl_Position;\n"
238 "	o_color     = color;\n"
239 "	EmitVertex();\n"
240 "\n"
241 "	gl_ViewportIndex = viewportIndex[0];\n"
242 "	gl_Position = gl_in[0].gl_Position + vec4(0.0, widthHeight[0].y, 0.0, 0.0);\n"
243 "	o_color     = color;\n"
244 "	EmitVertex();\n"
245 "\n"
246 "	gl_ViewportIndex = viewportIndex[0];\n"
247 "	gl_Position = gl_in[0].gl_Position + vec4(widthHeight[0].x, 0.0, 0.0, 0.0);\n"
248 "	o_color     = color;\n"
249 "	EmitVertex();\n"
250 "\n"
251 "	gl_ViewportIndex = viewportIndex[0];\n"
252 "	gl_Position = gl_in[0].gl_Position + vec4(widthHeight[0].xy, 0.0, 0.0);\n"
253 "	o_color     = color;\n"
254 "	EmitVertex();\n"
255 "\n"
256 "	EndPrimitive();\n"
257 "}\n";
258 
259 // Pass through fragment shader
260 const char frag_glsl[] =
261 "#version 460\n"
262 "layout(location=0) flat in vec4 color;\n"
263 "layout(location=0) out     vec4 o_color;\n"
264 "\n"
265 "void main()\n"
266 "{\n"
267 "	o_color = color;\n"
268 "}\n";
269 
270 static const VkVertexInputBindingDescription binding = {0, sizeof(Rectangle), VK_VERTEX_INPUT_RATE_VERTEX};
271 
272 static const VkVertexInputAttributeDescription attributes[4] = {
273 	{0, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Rectangle, xyz)},
274 	{1, 0, VK_FORMAT_R32_SINT, offsetof(Rectangle, r8g8b8)},
275 	{2, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(Rectangle, widthHeight)},
276 	{3, 0, VK_FORMAT_R32_SINT, offsetof(Rectangle, viewportIndex)} };
277 
278 static const VkPipelineVertexInputStateCreateInfo vertexInput = {
279 	VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, NULL,
280 	0, 1, &binding, 4, attributes };
281 
282 static const VkPipelineRasterizationStateCreateInfo rasterization = {
283 	VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, NULL,
284 	0,
285 	VK_FALSE,
286 	VK_FALSE,
287 	VK_POLYGON_MODE_FILL,
288 	VK_CULL_MODE_BACK_BIT,
289 	VK_FRONT_FACE_COUNTER_CLOCKWISE,
290 	VK_FALSE, 0.0f, 0.0f, 0.0f, 1.0f };
291 
292 static const VkPipelineDepthStencilStateCreateInfo depthStencil = {
293 	VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, NULL,
294 	0, VK_TRUE, VK_TRUE, VK_COMPARE_OP_LESS,
295 	0, 0, {}, {}, 0, 0 };
296 
297 static const VkPipelineColorBlendAttachmentState blendAttachment {
298 	VK_FALSE,
299 	VK_BLEND_FACTOR_ZERO, VK_BLEND_FACTOR_ZERO, VK_BLEND_OP_ADD,
300 	VK_BLEND_FACTOR_ZERO, VK_BLEND_FACTOR_ZERO, VK_BLEND_OP_ADD,
301 	VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT };
302 
303 static const VkPipelineColorBlendStateCreateInfo blend = {
304 	VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, NULL,
305 	0, VK_FALSE, VK_LOGIC_OP_CLEAR, 1, &blendAttachment, {} };
306 
307 static const VkDynamicState dynamicStateData[2] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
308 static const VkDynamicState dynamicStateWithCountData[2] = {
309 	VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT_EXT,
310 	VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT_EXT };
311 
312 static const VkPipelineDynamicStateCreateInfo dynamicState = {
313 	VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, NULL,
314 	0, 2, dynamicStateData };
315 
316 static const VkPipelineDynamicStateCreateInfo dynamicStateWithCount = {
317 	VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, NULL,
318 	0, 2, dynamicStateWithCountData };
319 
320 } // end namespace pipelinestate
321 
322 
323 const VkBufferCreateInfo rectangleBufferInfo = {
324 	VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, NULL, 0,
325 	kMaxRectangles * sizeof(Rectangle),
326 	VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_SHARING_MODE_EXCLUSIVE, 0, NULL };
327 
328 const VkBufferCreateInfo downloadBufferInfo = {
329 	VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, NULL, 0,
330 	kWidth * kHeight * sizeof(Texel),
331 	VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_SHARING_MODE_EXCLUSIVE, 0, NULL };
332 
333 const VkImageCreateInfo colorImageInfo = {
334 	VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, NULL, 0,
335 	VK_IMAGE_TYPE_2D,
336 	kFormat,
337 	{ kWidth, kHeight, 1 },
338 	1, 1, VK_SAMPLE_COUNT_1_BIT,
339 	VK_IMAGE_TILING_OPTIMAL,
340 	VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
341 	VK_SHARING_MODE_EXCLUSIVE, 0, NULL,
342 	VK_IMAGE_LAYOUT_UNDEFINED };
343 
makeDepthImageInfo(Context & context)344 VkImageCreateInfo makeDepthImageInfo(Context& context)
345 {
346 	VkImageCreateInfo info = {
347 		VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, NULL, 0,
348 		VK_IMAGE_TYPE_2D,
349 		VK_FORMAT_UNDEFINED, // To be filled in.
350 		{ kWidth, kHeight, 1 },
351 		1, 1, VK_SAMPLE_COUNT_1_BIT,
352 		VK_IMAGE_TILING_OPTIMAL,
353 		VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
354 		VK_SHARING_MODE_EXCLUSIVE, 0, NULL,
355 		VK_IMAGE_LAYOUT_UNDEFINED
356 	};
357 
358 	VkFormat depthFormats[4] = {
359 		VK_FORMAT_X8_D24_UNORM_PACK32, VK_FORMAT_D24_UNORM_S8_UINT,
360 		VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT};
361 	for (int i = 0; i < 4; ++i)
362 	{
363 		VkFormatProperties properties;
364 		context.getInstanceInterface().getPhysicalDeviceFormatProperties(
365 			context.getPhysicalDevice(), depthFormats[i], &properties);
366 		if (properties.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)
367 		{
368 			info.format = depthFormats[i];
369 			return info;
370 		}
371 	}
372 	throw std::runtime_error("Did not find suitable depth attachment format.");
373 }
374 
375 
376 // Initialize the Vulkan state for the tests.
InheritanceTestInstance(Context & context,PipelineConstructionType pipelineConstructionType,InheritanceMode inheritanceMode)377 InheritanceTestInstance::InheritanceTestInstance(Context& context, PipelineConstructionType pipelineConstructionType, InheritanceMode inheritanceMode)
378 	: TestInstance(context)
379 	, m_in(context.getInstanceInterface())
380 	, m_vk(context.getDeviceInterface())
381 	, m_inheritanceMode(inheritanceMode)
382 	, m_pipelineConstructionType(pipelineConstructionType)
383 	, m_rectangleBuffer(m_vk, m_context.getDevice(), m_context.getDefaultAllocator(), rectangleBufferInfo,
384 						MemoryRequirement::HostVisible | MemoryRequirement::Coherent)
385 	, m_downloadBuffer(m_vk, m_context.getDevice(), m_context.getDefaultAllocator(), downloadBufferInfo,
386 						MemoryRequirement::HostVisible | MemoryRequirement::Coherent)
387 	, m_depthImageInfo(makeDepthImageInfo(context))
388 	, m_colorImage(m_vk, m_context.getDevice(), m_context.getDefaultAllocator(), colorImageInfo, MemoryRequirement::Local)
389 	, m_depthImage(m_vk, m_context.getDevice(), m_context.getDefaultAllocator(), m_depthImageInfo, MemoryRequirement::Local)
390 	, m_colorViewInfo{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, NULL, 0, m_colorImage.get(), VK_IMAGE_VIEW_TYPE_2D,
391 					   kFormat, {}, { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 } }
392 	, m_depthViewInfo{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, NULL, 0, m_depthImage.get(), VK_IMAGE_VIEW_TYPE_2D,
393 					   m_depthImageInfo.format, {}, { VK_IMAGE_ASPECT_DEPTH_BIT, 0, 1, 0, 1 } }
394 	, m_colorView(createImageView(m_vk, m_context.getDevice(), &m_colorViewInfo, NULL))
395 	, m_depthView(createImageView(m_vk, m_context.getDevice(), &m_depthViewInfo, NULL))
396 {
397 	VkDevice dev = m_context.getDevice();
398 
399 	// Render pass, adapted from Alexander Overvoorde's
400 	// vulkan-tutorial.com (CC0 1.0 Universal)
401 	VkAttachmentDescription colorAttachment{};
402 	colorAttachment.format = kFormat;
403 	colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
404 	colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
405 	colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
406 	colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
407 	colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
408 	colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
409 	colorAttachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
410 
411 	VkAttachmentDescription depthAttachment{};
412 	depthAttachment.format = m_depthImageInfo.format;
413 	depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
414 	depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
415 	depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
416 	depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
417 	depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
418 	depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
419 	depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
420 
421 	VkAttachmentReference colorAttachmentRef{};
422 	colorAttachmentRef.attachment = 0;
423 	colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
424 
425 	VkAttachmentReference depthAttachmentRef{};
426 	depthAttachmentRef.attachment = 1;
427 	depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
428 
429 	VkSubpassDescription subpass{};
430 	subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
431 	subpass.colorAttachmentCount = 1;
432 	subpass.pColorAttachments = &colorAttachmentRef;
433 	subpass.pDepthStencilAttachment = &depthAttachmentRef;
434 
435 	VkSubpassDependency dependency{};
436 	dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
437 	dependency.dstSubpass = 0;
438 	dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
439 	dependency.srcAccessMask = 0;
440 	dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
441 	dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
442 
443 	VkAttachmentDescription attachments[2] = {colorAttachment, depthAttachment};
444 	VkRenderPassCreateInfo renderPassInfo{};
445 	renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
446 	renderPassInfo.attachmentCount = 2;
447 	renderPassInfo.pAttachments = attachments;
448 	renderPassInfo.subpassCount = 1;
449 	renderPassInfo.pSubpasses = &subpass;
450 	renderPassInfo.dependencyCount = 1;
451 	renderPassInfo.pDependencies = &dependency;
452 
453 	m_renderPass = RenderPassWrapper(pipelineConstructionType, m_vk, dev, &renderPassInfo);
454 
455 	// Set up framebuffer
456 	VkImageView attachmentViews[2] = { m_colorView.get(), m_depthView.get() };
457 	VkFramebufferCreateInfo framebufferInfo {
458 		VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
459 		NULL,
460 		0,
461 		m_renderPass.get(),
462 		2, attachmentViews,
463 		kWidth, kHeight, 1 };
464 	m_renderPass.createFramebuffer(m_vk, dev, &framebufferInfo, {*m_colorImage, *m_depthImage});
465 
466 	// Compile graphics pipeline stages.
467 	m_vertModule = vk::ShaderWrapper(m_vk, dev, m_context.getBinaryCollection().get("vert"), 0u);
468 	m_geomModule = vk::ShaderWrapper(m_vk, dev, m_context.getBinaryCollection().get("geom"), 0u);
469 	m_fragModule = vk::ShaderWrapper(m_vk, dev, m_context.getBinaryCollection().get("frag"), 0u);
470 
471 	// Set up pipeline layout (empty)
472 	VkPipelineLayoutCreateInfo pipelineLayoutInfo{ };
473 	pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
474 	m_rectanglePipelineLayout = PipelineLayoutWrapper(pipelineConstructionType, m_vk, dev, &pipelineLayoutInfo, NULL);
475 
476 	// Graphics pipelines are created on-the-fly later.
477 	deUint32 size = kMaxViewports + 1;
478 	m_rectanglePipelines.reserve(size);
479 	for (deUint32 i = 0; i < size; ++i)
480 		m_rectanglePipelines.emplace_back(m_context.getInstanceInterface(), m_vk, m_context.getPhysicalDevice(), m_context.getDevice(), m_context.getDeviceExtensions(), pipelineConstructionType);
481 
482 	// Command pool and command buffers.
483 	VkCommandPoolCreateInfo poolInfo {
484 		VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
485 		NULL,
486 		VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
487 		m_context.getUniversalQueueFamilyIndex() };
488 	m_cmdPool = createCommandPool(m_vk, dev, &poolInfo, NULL);
489 
490 	VkCommandBufferAllocateInfo cmdBufferInfo {
491 		VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, NULL,
492 		m_cmdPool.get(),
493 		VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1 };
494 	m_primaryCmdBuffer = allocateCommandBuffer(m_vk, dev, &cmdBufferInfo);
495 	cmdBufferInfo.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY;
496 	m_setStateCmdBuffer = allocateCommandBuffer(m_vk, dev, &cmdBufferInfo);
497 	m_subpassCmdBuffer = allocateCommandBuffer(m_vk, dev, &cmdBufferInfo);
498 }
499 
500 
u8_from_unorm(float x)501 static deUint8 u8_from_unorm(float x)
502 {
503 	return deUint8(roundf(de::clamp(x, 0.0f, 1.0f) * 255.0f));
504 }
505 
506 
507 // Start work (on the univeral queue) for filling m_downloadBuffer with the image
508 // resulting from rendering the test case. Must vkQueueWaitIdle before
509 // accessing the data, or calling this function again.
startRenderCmds(const TestGeometry & geometry)510 void InheritanceTestInstance::startRenderCmds(const TestGeometry& geometry)
511 {
512 	DE_ASSERT(geometry.viewports.size() > 0);
513 	DE_ASSERT(geometry.viewports.size() <= kMaxViewports);
514 	DE_ASSERT(geometry.viewports.size() == geometry.scissors.size());
515 
516 	// Fill vertex buffer
517 	DE_ASSERT(kMaxRectangles >= geometry.rectangles.size());
518 	Rectangle* pRectangles = static_cast<Rectangle*>(m_rectangleBuffer.getAllocation().getHostPtr());
519 	for (size_t i = 0; i < geometry.rectangles.size(); ++i)
520 	{
521 		pRectangles[i] = geometry.rectangles[i];
522 	}
523 
524 	VkCommandBufferInheritanceInfo inheritanceInfo {
525 		VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO,
526 		NULL,
527 		m_renderPass.get(),
528 		0,
529 		m_renderPass.getFramebuffer(),
530 		0, 0, 0 };
531 
532 	VkCommandBufferBeginInfo cmdBeginInfo {
533 		VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
534 		NULL,
535 		VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT,
536 		&inheritanceInfo };
537 
538 #ifndef CTS_USES_VULKANSC
539 	vk::VkCommandBufferInheritanceRenderingInfo inheritanceRenderingInfo = vk::initVulkanStructure();
540 	inheritanceRenderingInfo.flags = (VkRenderingFlags)0u;
541 	inheritanceRenderingInfo.viewMask = 0x0;
542 	inheritanceRenderingInfo.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
543 	std::vector<vk::VkFormat> colorFormats;
544 	if (vk::isConstructionTypeShaderObject(m_pipelineConstructionType))
545 	{
546 		m_renderPass.fillInheritanceRenderingInfo(0U, &colorFormats, &inheritanceRenderingInfo);
547 		inheritanceInfo.pNext = &inheritanceRenderingInfo;
548 	}
549 #endif
550 
551 	// ************************************************************************
552 	// Record state-setting secondary command buffer.
553 	// ************************************************************************
554 	VK_CHECK(m_vk.beginCommandBuffer(m_setStateCmdBuffer.get(), &cmdBeginInfo));
555 	switch (m_inheritanceMode)
556 	{
557 	case kInheritanceDisabled:
558 	case kInheritFromPrimary:
559 	case kInheritFromPrimaryWithCount:
560 		break;
561 	case kInheritFromSecondary:
562 		// Set all viewport/scissor state.
563 		if (vk::isConstructionTypeShaderObject(m_pipelineConstructionType))
564 		{
565 #ifndef CTS_USES_VULKANSC
566 			m_vk.cmdSetViewportWithCount(m_setStateCmdBuffer.get(), deUint32(geometry.viewports.size()), &geometry.viewports[0]);
567 			m_vk.cmdSetScissorWithCount(m_setStateCmdBuffer.get(), deUint32(geometry.scissors.size()), &geometry.scissors[0]);
568 #else
569 			m_vk.cmdSetViewportWithCountEXT(m_setStateCmdBuffer.get(), deUint32(geometry.viewports.size()), &geometry.viewports[0]);
570 			m_vk.cmdSetScissorWithCountEXT(m_setStateCmdBuffer.get(), deUint32(geometry.scissors.size()), &geometry.scissors[0]);
571 #endif
572 		}
573 		else
574 		{
575 			m_vk.cmdSetViewport(m_setStateCmdBuffer.get(), 0, deUint32(geometry.viewports.size()), &geometry.viewports[0]);
576 			m_vk.cmdSetScissor(m_setStateCmdBuffer.get(), 0, deUint32(geometry.scissors.size()), &geometry.scissors[0]);
577 		}
578 		break;
579 	case kSplitInheritance:
580 		// Set just the first viewport / scissor, rest are set in
581 		// primary command buffer. Checks that extension properly
582 		// muxes state from different sources.
583 		if (vk::isConstructionTypeShaderObject(m_pipelineConstructionType))
584 		{
585 #ifndef CTS_USES_VULKANSC
586 			m_vk.cmdSetViewportWithCount(m_setStateCmdBuffer.get(), 1, &geometry.viewports[0]);
587 			m_vk.cmdSetScissorWithCount(m_setStateCmdBuffer.get(), 1, &geometry.scissors[0]);
588 #else
589 			m_vk.cmdSetViewportWithCountEXT(m_setStateCmdBuffer.get(), 1, &geometry.viewports[0]);
590 			m_vk.cmdSetScissorWithCountEXT(m_setStateCmdBuffer.get(), 1, &geometry.scissors[0]);
591 #endif
592 		}
593 		else
594 		{
595 			m_vk.cmdSetViewport(m_setStateCmdBuffer.get(), 0, 1, &geometry.viewports[0]);
596 			m_vk.cmdSetScissor(m_setStateCmdBuffer.get(), 0, 1, &geometry.scissors[0]);
597 		}
598 		break;
599 	case kInheritFromSecondaryWithCount:
600 #ifndef CTS_USES_VULKANSC
601 		m_vk.cmdSetViewportWithCount(m_setStateCmdBuffer.get(),
602 									 deUint32(geometry.viewports.size()),
603 									 &geometry.viewports[0]);
604 		m_vk.cmdSetScissorWithCount(m_setStateCmdBuffer.get(),
605 									deUint32(geometry.scissors.size()),
606 									&geometry.scissors[0]);
607 #else
608 		m_vk.cmdSetViewportWithCountEXT(m_setStateCmdBuffer.get(),
609 									 deUint32(geometry.viewports.size()),
610 									 &geometry.viewports[0]);
611 		m_vk.cmdSetScissorWithCountEXT(m_setStateCmdBuffer.get(),
612 									deUint32(geometry.scissors.size()),
613 									&geometry.scissors[0]);
614 #endif // CTS_USES_VULKANSC
615 		break;
616 	}
617 	VK_CHECK(m_vk.endCommandBuffer(m_setStateCmdBuffer.get()));
618 
619 	// ************************************************************************
620 	// Record subpass command buffer, bind vertex buffer and pipeline,
621 	// then draw rectangles.
622 	// ************************************************************************
623 	if (m_inheritanceMode != kInheritanceDisabled)
624 	{
625 #ifndef CTS_USES_VULKANSC
626 		// Enable viewport/scissor inheritance struct.
627 		VkCommandBufferInheritanceViewportScissorInfoNV inheritViewportInfo{
628 			VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_VIEWPORT_SCISSOR_INFO_NV,
629 			inheritanceInfo.pNext,
630 			VK_TRUE,
631 			deUint32(geometry.viewports.size()), &geometry.viewports[0] };
632 		inheritanceInfo.pNext = &inheritViewportInfo;
633 		VK_CHECK(m_vk.beginCommandBuffer(m_subpassCmdBuffer.get(), &cmdBeginInfo));
634 		inheritanceInfo.pNext = inheritViewportInfo.pNext;
635 #endif // CTS_USES_VULKANSC
636 	}
637 	else
638 	{
639 		VK_CHECK(m_vk.beginCommandBuffer(m_subpassCmdBuffer.get(), &cmdBeginInfo));
640 	}
641 	// Set viewport/scissor state only when not inherited.
642 	if (m_inheritanceMode == kInheritanceDisabled)
643 	{
644 		if (vk::isConstructionTypeShaderObject(m_pipelineConstructionType))
645 		{
646 #ifndef CTS_USES_VULKANSC
647 			m_vk.cmdSetViewportWithCount(m_subpassCmdBuffer.get(), deUint32(geometry.viewports.size()), &geometry.viewports[0]);
648 			m_vk.cmdSetScissorWithCount(m_subpassCmdBuffer.get(), deUint32(geometry.scissors.size()), &geometry.scissors[0]);
649 #else
650 			m_vk.cmdSetViewportWithCountEXT(m_subpassCmdBuffer.get(), deUint32(geometry.viewports.size()), &geometry.viewports[0]);
651 			m_vk.cmdSetScissorWithCountEXT(m_subpassCmdBuffer.get(), deUint32(geometry.scissors.size()), &geometry.scissors[0]);
652 #endif
653 		}
654 		else
655 		{
656 			m_vk.cmdSetViewport(m_subpassCmdBuffer.get(), 0, deUint32(geometry.viewports.size()), &geometry.viewports[0]);
657 			m_vk.cmdSetScissor(m_subpassCmdBuffer.get(), 0, deUint32(geometry.scissors.size()), &geometry.scissors[0]);
658 		}
659 	}
660 	// Get the graphics pipeline, creating it if needed (encountered
661 	// new static viewport/scissor count). 0 = dynamic count.
662 	deUint32 staticViewportCount = 0;
663 	switch (m_inheritanceMode)
664 	{
665 	case kInheritanceDisabled:
666 	case kInheritFromPrimary:
667 	case kInheritFromSecondary:
668 	case kSplitInheritance:
669 		staticViewportCount = deUint32(geometry.viewports.size());
670 		break;
671 	case kInheritFromPrimaryWithCount:
672 	case kInheritFromSecondaryWithCount:
673 		staticViewportCount = 0;
674 		break;
675 	}
676 	DE_ASSERT(staticViewportCount < m_rectanglePipelines.size());
677 	if (!m_rectanglePipelines[staticViewportCount].wasPipelineOrShaderObjectBuild())
678 	{
679 		const std::vector<VkViewport>	viewports;
680 		const std::vector<VkRect2D>		scissors;
681 
682 		m_rectanglePipelines[staticViewportCount]
683 			.setDynamicState((staticViewportCount == 0) ? &pipelinestate::dynamicStateWithCount : &pipelinestate::dynamicState)
684 			.setDefaultTopology(VK_PRIMITIVE_TOPOLOGY_POINT_LIST)
685 			.setDefaultViewportsCount(staticViewportCount)
686 			.setDefaultScissorsCount(staticViewportCount)
687 			.setDefaultMultisampleState()
688 			.setDefaultColorBlendState()
689 			.setupVertexInputState(&pipelinestate::vertexInput)
690 			.setupPreRasterizationShaderState(viewports,
691 											  scissors,
692 											  m_rectanglePipelineLayout,
693 											  *m_renderPass,
694 											  0u,
695 											  m_vertModule,
696 											  &pipelinestate::rasterization,
697 											  vk::ShaderWrapper(),
698 											  vk::ShaderWrapper(),
699 											  m_geomModule)
700 			.setupFragmentShaderState(m_rectanglePipelineLayout,
701 									  *m_renderPass,
702 									  0u,
703 									  m_fragModule,
704 									  &pipelinestate::depthStencil)
705 			.setupFragmentOutputState(*m_renderPass, 0u, &pipelinestate::blend)
706 			.setMonolithicPipelineLayout(m_rectanglePipelineLayout)
707 			.buildPipeline();
708 	}
709 	m_rectanglePipelines[staticViewportCount].bind(m_subpassCmdBuffer.get());
710 
711 	// Bind vertex buffer and draw.
712 	VkDeviceSize offset = 0;
713 	VkBuffer     vertexBuffer = m_rectangleBuffer.get();
714 	m_vk.cmdBindVertexBuffers(m_subpassCmdBuffer.get(), 0, 1, &vertexBuffer, &offset);
715 	m_vk.cmdDraw(m_subpassCmdBuffer.get(), deUint32(geometry.rectangles.size()), 1, 0, 0);
716 	VK_CHECK(m_vk.endCommandBuffer(m_subpassCmdBuffer.get()));
717 
718 	// ************************************************************************
719 	// Primary command buffer commands, start render pass and execute
720 	// the secondary command buffers, then copy rendered image to
721 	// download buffer.
722 	// ************************************************************************
723 	VkCommandBufferBeginInfo beginInfo {
724 		VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
725 		NULL,
726 		0, NULL };
727 	VK_CHECK(m_vk.beginCommandBuffer(m_primaryCmdBuffer.get(), &beginInfo));
728 
729 	VkClearValue clearValues[2];
730 	clearValues[0].color.float32[0] = geometry.clearColor.x();
731 	clearValues[0].color.float32[1] = geometry.clearColor.y();
732 	clearValues[0].color.float32[2] = geometry.clearColor.z();
733 	clearValues[0].color.float32[3] = 1.0f;
734 	clearValues[1].depthStencil = { geometry.clearDepth, 0 };
735 
736 	VkRenderPassBeginInfo renderPassBeginInfo {
737 		VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
738 		NULL,
739 		m_renderPass.get(),
740 		m_renderPass.getFramebuffer(),
741 		{ { 0, 0 }, { kWidth, kHeight } },
742 		2, clearValues };
743 
744 	switch (m_inheritanceMode)
745 	{
746 	case kInheritFromPrimary:
747 		// Specify all viewport/scissor state only when we expect to.
748 		// inherit ALL viewport/scissor state from primary command buffer.
749 		if (vk::isConstructionTypeShaderObject(m_pipelineConstructionType))
750 		{
751 #ifndef CTS_USES_VULKANSC
752 			m_vk.cmdSetViewportWithCount(m_primaryCmdBuffer.get(), deUint32(geometry.viewports.size()), &geometry.viewports[0]);
753 			m_vk.cmdSetScissorWithCount(m_primaryCmdBuffer.get(), deUint32(geometry.scissors.size()), &geometry.scissors[0]);
754 #else
755 			m_vk.cmdSetViewportWithCountEXT(m_primaryCmdBuffer.get(), deUint32(geometry.viewports.size()), &geometry.viewports[0]);
756 			m_vk.cmdSetScissorWithCountEXT(m_primaryCmdBuffer.get(), deUint32(geometry.scissors.size()), &geometry.scissors[0]);
757 #endif
758 		}
759 		else
760 		{
761 			m_vk.cmdSetViewport(m_primaryCmdBuffer.get(), 0, deUint32(geometry.viewports.size()), &geometry.viewports[0]);
762 			m_vk.cmdSetScissor(m_primaryCmdBuffer.get(), 0, deUint32(geometry.scissors.size()), &geometry.scissors[0]);
763 		}
764 		break;
765 	case kInheritFromPrimaryWithCount:
766 		// Same but with count inherited.
767 #ifndef CTS_USES_VULKANSC
768 		m_vk.cmdSetViewportWithCount(m_primaryCmdBuffer.get(),
769 									 deUint32(geometry.viewports.size()),
770 									 &geometry.viewports[0]);
771 		m_vk.cmdSetScissorWithCount(m_primaryCmdBuffer.get(),
772 									deUint32(geometry.scissors.size()),
773 									&geometry.scissors[0]);
774 #else
775 		m_vk.cmdSetViewportWithCountEXT(m_primaryCmdBuffer.get(),
776 									 deUint32(geometry.viewports.size()),
777 									 &geometry.viewports[0]);
778 		m_vk.cmdSetScissorWithCountEXT(m_primaryCmdBuffer.get(),
779 									deUint32(geometry.scissors.size()),
780 									&geometry.scissors[0]);
781 #endif // CTS_USES_VULKANSC
782 		break;
783 	case kSplitInheritance:
784 		// Specify the remaining viewport, scissors not set by the
785 		// setStateCmdBuffer in this test mode.
786 		if (geometry.viewports.size() > 1)
787 		{
788 			if (vk::isConstructionTypeShaderObject(m_pipelineConstructionType))
789 			{
790 #ifndef CTS_USES_VULKANSC
791 				m_vk.cmdSetViewportWithCount(m_primaryCmdBuffer.get(), deUint32(geometry.viewports.size()), &geometry.viewports[0]);
792 				m_vk.cmdSetScissorWithCount(m_primaryCmdBuffer.get(), deUint32(geometry.scissors.size()), &geometry.scissors[0]);
793 #else
794 				m_vk.cmdSetViewportWithCountEXT(m_primaryCmdBuffer.get(), deUint32(geometry.viewports.size()), &geometry.viewports[0]);
795 				m_vk.cmdSetScissorWithCountEXT(m_primaryCmdBuffer.get(), deUint32(geometry.scissors.size()), &geometry.scissors[0]);
796 #endif
797 			}
798 
799 			m_vk.cmdSetViewport(m_primaryCmdBuffer.get(), 1, deUint32(geometry.viewports.size() - 1), &geometry.viewports[1]);
800 			m_vk.cmdSetScissor(m_primaryCmdBuffer.get(), 1, deUint32(geometry.scissors.size() - 1), &geometry.scissors[1]);
801 		}
802 		/* FALLTHROUGH */
803 	case kInheritanceDisabled:
804 	case kInheritFromSecondary:
805 	case kInheritFromSecondaryWithCount:
806 		// Specify some bogus state, ensure correctly overwritten later.
807 		VkViewport bogusViewport { 0.f, 0.f, 8.f, 8.f, 0.f, 0.1f };
808 		VkRect2D   bogusScissors { { 2, 0 }, { 100, 100 }};
809 		if (vk::isConstructionTypeShaderObject(m_pipelineConstructionType))
810 		{
811 #ifndef CTS_USES_VULKANSC
812 			m_vk.cmdSetViewportWithCount(m_primaryCmdBuffer.get(), 1, &bogusViewport);
813 			m_vk.cmdSetScissorWithCount(m_primaryCmdBuffer.get(), 1, &bogusScissors);
814 #else
815 			m_vk.cmdSetViewportWithCountEXT(m_primaryCmdBuffer.get(), 1, &bogusViewport);
816 			m_vk.cmdSetScissorWithCountEXT(m_primaryCmdBuffer.get(), 1, &bogusScissors);
817 #endif
818 		}
819 		else
820 		{
821 			m_vk.cmdSetViewport(m_primaryCmdBuffer.get(), 0, 1, &bogusViewport);
822 			m_vk.cmdSetScissor(m_primaryCmdBuffer.get(), 0, 1, &bogusScissors);
823 		}
824 		break;
825 	}
826 
827 	m_renderPass.begin(m_vk, m_primaryCmdBuffer.get(), renderPassBeginInfo.renderArea, renderPassBeginInfo.clearValueCount, renderPassBeginInfo.pClearValues, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS);
828 	VkCommandBuffer secondaryCmdBuffers[2] = {m_setStateCmdBuffer.get(),
829 											  m_subpassCmdBuffer.get()};
830 	m_vk.cmdExecuteCommands(m_primaryCmdBuffer.get(), 2, secondaryCmdBuffers);
831 	m_renderPass.end(m_vk, m_primaryCmdBuffer.get());
832 
833 	// Barrier, then copy rendered image to download buffer.
834 	VkImageMemoryBarrier imageBarrier {
835 		VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
836 		NULL,
837 		VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
838 		VK_ACCESS_TRANSFER_READ_BIT,
839 		VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
840 		VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
841 		0, 0,
842 		m_colorImage.get(),
843 		{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }};
844 	m_vk.cmdPipelineBarrier(
845 		m_primaryCmdBuffer.get(),
846 		VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
847 		VK_PIPELINE_STAGE_TRANSFER_BIT,
848 		0, 0, NULL, 0, NULL, 1, &imageBarrier );
849 	VkBufferImageCopy bufferImageCopy {
850 		0, 0, 0,
851 		{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 },
852 		{ 0, 0, 0 },
853 		{ kWidth, kHeight, 1 } };
854 	m_vk.cmdCopyImageToBuffer(
855 		m_primaryCmdBuffer.get(),
856 		m_colorImage.get(),
857 		VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
858 		m_downloadBuffer.get(),
859 		1, &bufferImageCopy);
860 
861 	// Barrier, make buffer visible to host.
862 	VkBufferMemoryBarrier bufferBarrier {
863 		VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
864 		NULL,
865 		VK_ACCESS_TRANSFER_WRITE_BIT,
866 		VK_ACCESS_HOST_READ_BIT,
867 		0, 0,
868 		m_downloadBuffer.get(),
869 		0, VK_WHOLE_SIZE };
870 	m_vk.cmdPipelineBarrier(
871 		m_primaryCmdBuffer.get(),
872 		VK_PIPELINE_STAGE_TRANSFER_BIT,
873 		VK_PIPELINE_STAGE_HOST_BIT,
874 		0, 0, NULL, 1, &bufferBarrier, 0, NULL);
875 
876 	// End and submit primary command buffer.
877 	VK_CHECK(m_vk.endCommandBuffer(m_primaryCmdBuffer.get()));
878 	VkCommandBuffer primaryCmd = m_primaryCmdBuffer.get();
879 	VkSubmitInfo submitInfo {
880 		VK_STRUCTURE_TYPE_SUBMIT_INFO,
881 		NULL,
882 		0, NULL, NULL,
883 		1, &primaryCmd,
884 		0, NULL };
885 	m_vk.queueSubmit(m_context.getUniversalQueue(), 1, &submitInfo, 0);
886 }
887 
888 
rasterizeExpectedResults(const TestGeometry & geometry,Texel (& output)[kHeight][kWidth])889 void InheritanceTestInstance::rasterizeExpectedResults(const TestGeometry& geometry, Texel (&output)[kHeight][kWidth])
890 {
891 	// Clear color and depth buffers.
892 	Texel clearColorTexel{u8_from_unorm(geometry.clearColor.z()),
893 						  u8_from_unorm(geometry.clearColor.y()),
894 						  u8_from_unorm(geometry.clearColor.x()), 0u};
895 	for (size_t y = 0; y < kHeight; ++y)
896 	{
897 		for (size_t x = 0; x < kWidth; ++x)
898 		{
899 			m_cpuDepthBuffer[y][x] = geometry.clearDepth;
900 			output[y][x]           = clearColorTexel;
901 		}
902 	}
903 
904 	// Rasterize each rectangle. Pixels have half-integer centers.
905 	for (size_t i = 0; i < geometry.rectangles.size(); ++i) {
906 		Rectangle r = geometry.rectangles[i];
907 
908 		// Select correct viewport and scissor.
909 		VkViewport viewport = geometry.viewports.at(r.viewportIndex);
910 		VkRect2D   scissor  = geometry.scissors.at(r.viewportIndex);
911 
912 		// Transform xyz and width/height with selected viewport.
913 		float ox = viewport.x + viewport.width * 0.5f;
914 		float oy = viewport.y + viewport.height * 0.5f;
915 		float oz = viewport.minDepth;
916 
917 		float px = viewport.width;
918 		float py = viewport.height;
919 		float pz = viewport.maxDepth - viewport.minDepth;
920 
921 		float xLow  = de::clamp(r.xyz.x(), -1.0f, 1.0f);
922 		float xHigh = de::clamp(r.xyz.x() + r.widthHeight.x(), -1.0f, 1.0f);
923 		float yLow  = de::clamp(r.xyz.y(), -1.0f, 1.0f);
924 		float yHigh = de::clamp(r.xyz.y() + r.widthHeight.y(), -1.0f, 1.0f);
925 
926 		float xf[2];
927 		xf[0]    = px * 0.5f * xLow  + ox;
928 		xf[1]    = px * 0.5f * xHigh + ox;
929 		float yf[2];
930 		yf[0]    = py * 0.5f * yLow  + oy;
931 		yf[1]    = py * 0.5f * yHigh + oy;
932 		float zf = pz * r.xyz.z() + oz;
933 
934 		deInt32 xBegin = deInt32(floorf(xf[0] + 0.5f));
935 		deInt32 xEnd   = deInt32(floorf(xf[1] + 0.5f));
936 		deInt32 yBegin = deInt32(floorf(yf[0] + 0.5f));
937 		deInt32 yEnd   = deInt32(floorf(yf[1] + 0.5f));
938 
939 		// Scissor test, only correct when drawn rectangle has
940 		// positive width/height.
941 		deInt32 xsLow  = scissor.offset.x;
942 		deInt32 xsHigh = xsLow + deInt32(scissor.extent.width);
943 		xBegin         = de::clamp(xBegin, xsLow, xsHigh);
944 		xEnd           = de::clamp(xEnd,   xsLow, xsHigh);
945 		deInt32 ysLow  = scissor.offset.y;
946 		deInt32 ysHigh = ysLow + deInt32(scissor.extent.height);
947 		yBegin         = de::clamp(yBegin, ysLow, ysHigh);
948 		yEnd           = de::clamp(yEnd,   ysLow, ysHigh);
949 
950 		// Clamp to framebuffer size
951 		xBegin = de::clamp(xBegin, 0, kWidth);
952 		xEnd   = de::clamp(xEnd,   0, kWidth);
953 		yBegin = de::clamp(yBegin, 0, kHeight);
954 		yEnd   = de::clamp(yEnd,   0, kHeight);
955 
956 		// Rasterize.
957 		Texel rectTexel = texelFrom_r8g8b8(r.r8g8b8);
958 		for (deInt32 x = xBegin; x < xEnd; ++x)
959 		{
960 			for (deInt32 y = yBegin; y < yEnd; ++y)
961 			{
962 				// Depth test
963 				float oldDepth = m_cpuDepthBuffer[y][x];
964 				if (!(zf < oldDepth)) continue;
965 
966 				output[y][x]           = rectTexel;
967 				m_cpuDepthBuffer[y][x] = zf;
968 			}
969 		}
970 	}
971 }
972 
973 
makeGeometry()974 std::vector<TestGeometry> makeGeometry()
975 {
976 	std::vector<TestGeometry> cases;
977 
978 	TestGeometry geometry;
979 	geometry.clearColor = Vec3(1.0f, 1.0f, 1.0f);
980 	geometry.clearDepth = 1.0f;
981 
982 	// Simple test case, three squares, the last one should go in
983 	// between the first two in depth due to viewport 1 halving the
984 	// actual depth value.
985 	geometry.rectangles.push_back(Rectangle{
986 		Vec3(-0.5f, -1.0f, 0.2f),
987 		0xFF0000,
988 		Vec2(0.5f, 1.0f),
989 		0 });
990 	geometry.rectangles.push_back(Rectangle{
991 		Vec3(0.0f, 0.0f, 0.6f),
992 		0x0000FF,
993 		Vec2(0.5f, 1.0f),
994 		0 });
995 	geometry.rectangles.push_back(Rectangle{
996 		Vec3(-0.25f, -0.5f, 0.8f), // becomes 0.4f depth
997 		0x008000,
998 		Vec2(0.5f, 1.0f),
999 		1 });
1000 	geometry.viewports.push_back({0, 0, kWidth, kHeight, 0.0f, 1.0f});
1001 	geometry.viewports.push_back({0, 0, kWidth, kHeight, 0.0f, 0.5f});
1002 	geometry.scissors.push_back({{0, 0}, {kWidth, kHeight}});
1003 	geometry.scissors.push_back({{0, 0}, {kWidth, kHeight}});
1004 
1005 	cases.push_back(geometry);
1006 
1007 	// Apply scissor rectangle to red and blue squares.
1008 	geometry.scissors[0].extent.width = kWidth / 2 + 1;
1009 	cases.push_back(geometry);
1010 
1011 	// Squash down and offset green rectangle's viewport.
1012 	geometry.viewports[1].y      = kHeight * 0.25f;
1013 	geometry.viewports[1].height = kHeight * 0.75f;
1014 	cases.push_back(geometry);
1015 
1016 	// Add another viewport and scissor.
1017 	geometry.viewports.push_back(
1018 		{kWidth / 2 - 4, 0, kWidth / 2, kHeight - 8, 0.5f, 1.0f});
1019 	geometry.scissors.push_back(
1020 		{{kWidth / 2 - 2, 10}, {kWidth / 2, kHeight}});
1021 	geometry.rectangles.push_back(Rectangle{
1022 		Vec3(-1.0f, -1.0f, 0.5f), // Becomes 0.75f depth
1023 		0x000000,
1024 		Vec2(1.75f, 1.75f),
1025 		2 });
1026 	cases.push_back(geometry);
1027 
1028 	// Add a few more rectangles.
1029 	geometry.rectangles.push_back(Rectangle{
1030 		Vec3(-0.25f, -0.25f, 0.1f),
1031 		0xFF00FF,
1032 		Vec2(0.375f, 0.375f),
1033 		0 });
1034 	geometry.rectangles.push_back(Rectangle{
1035 		Vec3(-1.0f, -1.0f, 0.8f), // Becomes 0.9f depth
1036 		0x00FFFF,
1037 		Vec2(2.0f, 2.0f),
1038 		2 });
1039 	geometry.rectangles.push_back(Rectangle{
1040 		Vec3(-1.0f, -1.0f, 0.7f),
1041 		0x808000,
1042 		Vec2(2.0f, 2.0f),
1043 		0 });
1044 	cases.push_back(geometry);
1045 
1046 	// Change clear depth and color.
1047 	geometry.clearDepth = 0.85f;
1048 	geometry.clearColor = Vec3(1.0f, 1.0f, 0.0f);
1049 	cases.push_back(geometry);
1050 
1051 	// Alter viewport/scissor 2.
1052 	geometry.viewports[2] = VkViewport{ 0, 0, kWidth, kHeight, 0.51f, 0.53f };
1053 	geometry.scissors[2]  = VkRect2D{ { 20, 0 }, { kWidth, kHeight } };
1054 	cases.push_back(geometry);
1055 
1056 	// Change clear depth and color again.
1057 	geometry.clearDepth = 0.5f;
1058 	geometry.clearColor = Vec3(0.0f, 1.0f, 0.0f);
1059 	cases.push_back(geometry);
1060 
1061 	return cases;
1062 }
1063 
1064 
iterate(void)1065 tcu::TestStatus InheritanceTestInstance::iterate(void)
1066 {
1067 	std::vector<TestGeometry> testGeometries = makeGeometry();
1068 	deUint32 failBits = 0;
1069 	DE_ASSERT(testGeometries.size() < 32);
1070 
1071 	for (size_t i = 0; i != testGeometries.size(); ++i)
1072 	{
1073 		const TestGeometry& geometry = testGeometries[i];
1074 		TestResults results;
1075 
1076 		// Start drawing commands.
1077 		startRenderCmds(geometry);
1078 
1079 		// Work on CPU-side expected results while waiting for device.
1080 		rasterizeExpectedResults(geometry, results.expectedResult);
1081 
1082 		// Wait for commands to finish and copy back results.
1083 		m_vk.queueWaitIdle(m_context.getUniversalQueue());
1084 		memcpy(results.deviceResult,
1085 			   m_downloadBuffer.getAllocation().getHostPtr(),
1086 			   kWidth * kHeight * sizeof(Texel));
1087 
1088 		// Compare results. The test cases should be simple enough not to
1089 		// require fuzzy matching (power of 2 framebuffer, no nearby depth
1090 		// values, etc.)
1091 		bool passed = true;
1092 		for (size_t y = 0; y < kHeight; ++y)
1093 		{
1094 			for (size_t x = 0; x < kWidth; ++x)
1095 			{
1096 				passed &= results.expectedResult[y][x].red   == results.deviceResult[y][x].red;
1097 				passed &= results.expectedResult[y][x].green == results.deviceResult[y][x].green;
1098 				passed &= results.expectedResult[y][x].blue  == results.deviceResult[y][x].blue;
1099 			}
1100 		}
1101 		results.passed = passed; // Log results?
1102 
1103 		failBits |= deUint32(!passed) << i;
1104 	}
1105 
1106 	if (failBits != 0)
1107 	{
1108 		std::stringstream stream;
1109 		stream << "Failed for test geometry";
1110 		for (int i = 0; i < 32; ++i)
1111 		{
1112 			if (1 & (failBits >> i))
1113 			{
1114 				stream << ' ' << i;
1115 			}
1116 		}
1117 		return tcu::TestStatus::fail(stream.str());
1118 	}
1119 	else
1120 	{
1121 		return tcu::TestStatus::pass("pass");
1122 	}
1123 }
1124 
1125 
1126 class InheritanceTestCase : public TestCase
1127 {
1128 public:
InheritanceTestCase(tcu::TestContext & testCtx,vk::PipelineConstructionType pipelineConstructionType,InheritanceMode inheritanceMode,const char * name)1129 	InheritanceTestCase (tcu::TestContext& testCtx,
1130 						 vk::PipelineConstructionType pipelineConstructionType, InheritanceMode inheritanceMode,
1131 						 const char* name)
1132 		: TestCase(testCtx, name)
1133 		, m_pipelineConstructionType	(pipelineConstructionType)
1134 		, m_inheritanceMode				(inheritanceMode)
1135 	{
1136 
1137 	}
1138 
createInstance(Context & context) const1139 	TestInstance* createInstance (Context& context) const
1140 	{
1141 		return new InheritanceTestInstance(context, m_pipelineConstructionType, m_inheritanceMode);
1142 	}
1143 
checkSupport(Context & context) const1144 	virtual void checkSupport (Context& context) const
1145 	{
1146 		context.requireDeviceFunctionality("VK_NV_inherited_viewport_scissor");
1147 		if (m_inheritanceMode == kInheritFromPrimaryWithCount || m_inheritanceMode == kInheritFromSecondaryWithCount)
1148 		{
1149 			context.requireDeviceFunctionality("VK_EXT_extended_dynamic_state");
1150 		}
1151 		checkPipelineConstructionRequirements(context.getInstanceInterface(), context.getPhysicalDevice(), m_pipelineConstructionType);
1152 	}
1153 
initPrograms(vk::SourceCollections & programCollection) const1154 	virtual void initPrograms (vk::SourceCollections& programCollection) const
1155 	{
1156 		programCollection.glslSources.add("vert") << glu::VertexSource  (pipelinestate::vert_glsl);
1157 		programCollection.glslSources.add("geom") << glu::GeometrySource(pipelinestate::geom_glsl);
1158 		programCollection.glslSources.add("frag") << glu::FragmentSource(pipelinestate::frag_glsl);
1159 	}
1160 private:
1161 	vk::PipelineConstructionType	m_pipelineConstructionType;
1162 	InheritanceMode					m_inheritanceMode;
1163 };
1164 
1165 } // anonymous namespace
1166 
1167 // Tests for inherited viewport/scissor state
DynamicStateInheritanceTests(tcu::TestContext & testCtx,vk::PipelineConstructionType pipelineConstructionType)1168 DynamicStateInheritanceTests::DynamicStateInheritanceTests (tcu::TestContext& testCtx, vk::PipelineConstructionType pipelineConstructionType)
1169 	: TestCaseGroup					(testCtx, "inheritance")
1170 	, m_pipelineConstructionType	(pipelineConstructionType)
1171 {
1172 
1173 }
1174 
init(void)1175 void DynamicStateInheritanceTests::init (void)
1176 {
1177 	// Baseline, no viewport/scissor inheritance
1178 	addChild(new InheritanceTestCase(m_testCtx, m_pipelineConstructionType, kInheritanceDisabled, "baseline"));
1179 #ifndef CTS_USES_VULKANSC
1180 	// Inherit viewport/scissor from calling primary command buffer
1181 	addChild(new InheritanceTestCase(m_testCtx, m_pipelineConstructionType, kInheritFromPrimary, "primary"));
1182 	// Inherit viewport/scissor from another secondary command buffer
1183 	addChild(new InheritanceTestCase(m_testCtx, m_pipelineConstructionType, kInheritFromSecondary, "secondary"));
1184 	// Inherit some viewports/scissors from primary, some from secondary
1185 	addChild(new InheritanceTestCase(m_testCtx, m_pipelineConstructionType, kSplitInheritance, "split"));
1186 	// Inherit viewport/scissor with count from calling primary command buffer
1187 	addChild(new InheritanceTestCase(m_testCtx, m_pipelineConstructionType, kInheritFromPrimaryWithCount, "primary_with_count"));
1188 	// Inherit viewport/scissor with count from another secondary command buffer
1189 	addChild(new InheritanceTestCase(m_testCtx, m_pipelineConstructionType, kInheritFromSecondaryWithCount, "secondary_with_count"));
1190 #endif // CTS_USES_VULKANSC
1191 }
1192 
1193 } // DynamicState
1194 } // vkt
1195