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