1 /*------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2016 The Khronos Group Inc.
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 vktPipelineMultisampleBaseResolveAndPerSampleFetch.cpp
23 * \brief Base class for tests that check results of multisample resolve
24 *          and/or values of individual samples
25 *//*--------------------------------------------------------------------*/
26 
27 #include "vktPipelineMultisampleBaseResolveAndPerSampleFetch.hpp"
28 #include "vktPipelineMakeUtil.hpp"
29 #include "vkBarrierUtil.hpp"
30 #include "vkBuilderUtil.hpp"
31 #include "vkQueryUtil.hpp"
32 #include "vkTypeUtil.hpp"
33 #include "vkCmdUtil.hpp"
34 #include "vkObjUtil.hpp"
35 #include "vkBufferWithMemory.hpp"
36 #include "vkImageWithMemory.hpp"
37 #include "tcuTestLog.hpp"
38 #include <vector>
39 
40 namespace vkt
41 {
42 namespace pipeline
43 {
44 namespace multisample
45 {
46 
47 using namespace vk;
48 
initPrograms(vk::SourceCollections & programCollection) const49 void MSCaseBaseResolveAndPerSampleFetch::initPrograms(vk::SourceCollections &programCollection) const
50 {
51     // Create vertex shader
52     std::ostringstream vs;
53 
54     vs << "#version 440\n"
55        << "layout(location = 0) in vec4 vs_in_position_ndc;\n"
56        << "\n"
57        << "out gl_PerVertex {\n"
58        << "    vec4  gl_Position;\n"
59        << "};\n"
60        << "void main (void)\n"
61        << "{\n"
62        << "    gl_Position = vs_in_position_ndc;\n"
63        << "}\n";
64 
65     programCollection.glslSources.add("per_sample_fetch_vs") << glu::VertexSource(vs.str());
66 
67     // Create fragment shader
68     std::ostringstream fs;
69 
70     fs << "#version 440\n"
71        << "\n"
72        << "layout(location = 0) out vec4 fs_out_color;\n"
73        << "\n"
74        << "layout(set = 0, binding = 0, input_attachment_index = 0) uniform subpassInputMS imageMS;\n"
75        << "\n"
76        << "layout(set = 0, binding = 1, std140) uniform SampleBlock {\n"
77        << "    int sampleNdx;\n"
78        << "};\n"
79        << "void main (void)\n"
80        << "{\n"
81        << "    fs_out_color = subpassLoad(imageMS, sampleNdx);\n"
82        << "}\n";
83 
84     programCollection.glslSources.add("per_sample_fetch_fs") << glu::FragmentSource(fs.str());
85 }
86 
MSInstanceBaseResolveAndPerSampleFetch(Context & context,const ImageMSParams & imageMSParams)87 MSInstanceBaseResolveAndPerSampleFetch::MSInstanceBaseResolveAndPerSampleFetch(Context &context,
88                                                                                const ImageMSParams &imageMSParams)
89     : MultisampleInstanceBase(context, imageMSParams)
90 {
91 }
92 
getMSStateCreateInfo(const ImageMSParams & imageMSParams) const93 VkPipelineMultisampleStateCreateInfo MSInstanceBaseResolveAndPerSampleFetch::getMSStateCreateInfo(
94     const ImageMSParams &imageMSParams) const
95 {
96     const VkPipelineMultisampleStateCreateInfo multisampleStateInfo = {
97         VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, // VkStructureType sType;
98         DE_NULL,                                                  // const void* pNext;
99         (VkPipelineMultisampleStateCreateFlags)0u,                // VkPipelineMultisampleStateCreateFlags flags;
100         imageMSParams.numSamples,                                 // VkSampleCountFlagBits rasterizationSamples;
101         VK_TRUE,                                                  // VkBool32 sampleShadingEnable;
102         imageMSParams.shadingRate,                                // float minSampleShading;
103         DE_NULL,                                                  // const VkSampleMask* pSampleMask;
104         VK_FALSE,                                                 // VkBool32 alphaToCoverageEnable;
105         VK_FALSE,                                                 // VkBool32 alphaToOneEnable;
106     };
107 
108     return multisampleStateInfo;
109 }
110 
createMSPassDescSetLayout(const ImageMSParams & imageMSParams)111 const VkDescriptorSetLayout *MSInstanceBaseResolveAndPerSampleFetch::createMSPassDescSetLayout(
112     const ImageMSParams &imageMSParams)
113 {
114     DE_UNREF(imageMSParams);
115 
116     return DE_NULL;
117 }
118 
createMSPassDescSet(const ImageMSParams & imageMSParams,const VkDescriptorSetLayout * descSetLayout)119 const VkDescriptorSet *MSInstanceBaseResolveAndPerSampleFetch::createMSPassDescSet(
120     const ImageMSParams &imageMSParams, const VkDescriptorSetLayout *descSetLayout)
121 {
122     DE_UNREF(imageMSParams);
123     DE_UNREF(descSetLayout);
124 
125     return DE_NULL;
126 }
127 
iterate(void)128 tcu::TestStatus MSInstanceBaseResolveAndPerSampleFetch::iterate(void)
129 {
130     const InstanceInterface &instance      = m_context.getInstanceInterface();
131     const DeviceInterface &deviceInterface = m_context.getDeviceInterface();
132     const VkDevice device                  = m_context.getDevice();
133     const VkPhysicalDevice physicalDevice  = m_context.getPhysicalDevice();
134     Allocator &allocator                   = m_context.getDefaultAllocator();
135     const VkQueue queue                    = m_context.getUniversalQueue();
136     const uint32_t queueFamilyIndex        = m_context.getUniversalQueueFamilyIndex();
137 
138     VkImageCreateInfo imageMSInfo;
139     VkImageCreateInfo imageRSInfo;
140     const uint32_t firstSubpassAttachmentsCount = 2u;
141 
142     // Check if image size does not exceed device limits
143     validateImageSize(instance, physicalDevice, m_imageType, m_imageMSParams.imageSize);
144 
145     // Check if device supports image format as color attachment
146     validateImageFeatureFlags(instance, physicalDevice, mapTextureFormat(m_imageFormat),
147                               VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT);
148 
149     imageMSInfo.sType                 = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
150     imageMSInfo.pNext                 = DE_NULL;
151     imageMSInfo.flags                 = 0u;
152     imageMSInfo.imageType             = mapImageType(m_imageType);
153     imageMSInfo.format                = mapTextureFormat(m_imageFormat);
154     imageMSInfo.extent                = makeExtent3D(getLayerSize(m_imageType, m_imageMSParams.imageSize));
155     imageMSInfo.arrayLayers           = getNumLayers(m_imageType, m_imageMSParams.imageSize);
156     imageMSInfo.mipLevels             = 1u;
157     imageMSInfo.samples               = m_imageMSParams.numSamples;
158     imageMSInfo.tiling                = VK_IMAGE_TILING_OPTIMAL;
159     imageMSInfo.initialLayout         = VK_IMAGE_LAYOUT_UNDEFINED;
160     imageMSInfo.usage                 = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
161     imageMSInfo.sharingMode           = VK_SHARING_MODE_EXCLUSIVE;
162     imageMSInfo.queueFamilyIndexCount = 0u;
163     imageMSInfo.pQueueFamilyIndices   = DE_NULL;
164 
165     if (m_imageType == IMAGE_TYPE_CUBE || m_imageType == IMAGE_TYPE_CUBE_ARRAY)
166     {
167         imageMSInfo.flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
168     }
169 
170     validateImageInfo(instance, physicalDevice, imageMSInfo);
171 
172     const de::UniquePtr<ImageWithMemory> imageMS(
173         new ImageWithMemory(deviceInterface, device, allocator, imageMSInfo, MemoryRequirement::Any));
174 
175     imageRSInfo         = imageMSInfo;
176     imageRSInfo.samples = VK_SAMPLE_COUNT_1_BIT;
177     imageRSInfo.usage   = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
178 
179     validateImageInfo(instance, physicalDevice, imageRSInfo);
180 
181     const de::UniquePtr<ImageWithMemory> imageRS(
182         new ImageWithMemory(deviceInterface, device, allocator, imageRSInfo, MemoryRequirement::Any));
183 
184     const uint32_t numSamples = static_cast<uint32_t>(imageMSInfo.samples);
185 
186     std::vector<de::SharedPtr<ImageWithMemory>> imagesPerSampleVec(numSamples);
187 
188     for (uint32_t sampleNdx = 0u; sampleNdx < numSamples; ++sampleNdx)
189     {
190         imagesPerSampleVec[sampleNdx] = de::SharedPtr<ImageWithMemory>(
191             new ImageWithMemory(deviceInterface, device, allocator, imageRSInfo, MemoryRequirement::Any));
192     }
193 
194     // Create render pass
195     std::vector<VkAttachmentDescription> attachments(firstSubpassAttachmentsCount + numSamples);
196 
197     {
198         const VkAttachmentDescription attachmentMSDesc = {
199             (VkAttachmentDescriptionFlags)0u,         // VkAttachmentDescriptionFlags flags;
200             imageMSInfo.format,                       // VkFormat format;
201             imageMSInfo.samples,                      // VkSampleCountFlagBits samples;
202             VK_ATTACHMENT_LOAD_OP_CLEAR,              // VkAttachmentLoadOp loadOp;
203             VK_ATTACHMENT_STORE_OP_STORE,             // VkAttachmentStoreOp storeOp;
204             VK_ATTACHMENT_LOAD_OP_DONT_CARE,          // VkAttachmentLoadOp stencilLoadOp;
205             VK_ATTACHMENT_STORE_OP_DONT_CARE,         // VkAttachmentStoreOp stencilStoreOp;
206             VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // VkImageLayout initialLayout;
207             VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL  // VkImageLayout finalLayout;
208         };
209 
210         attachments[0] = attachmentMSDesc;
211 
212         const VkAttachmentDescription attachmentRSDesc = {
213             (VkAttachmentDescriptionFlags)0u,         // VkAttachmentDescriptionFlags flags;
214             imageRSInfo.format,                       // VkFormat format;
215             imageRSInfo.samples,                      // VkSampleCountFlagBits samples;
216             VK_ATTACHMENT_LOAD_OP_CLEAR,              // VkAttachmentLoadOp loadOp;
217             VK_ATTACHMENT_STORE_OP_STORE,             // VkAttachmentStoreOp storeOp;
218             VK_ATTACHMENT_LOAD_OP_DONT_CARE,          // VkAttachmentLoadOp stencilLoadOp;
219             VK_ATTACHMENT_STORE_OP_DONT_CARE,         // VkAttachmentStoreOp stencilStoreOp;
220             VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // VkImageLayout initialLayout;
221             VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL  // VkImageLayout finalLayout;
222         };
223 
224         attachments[1] = attachmentRSDesc;
225 
226         for (uint32_t sampleNdx = 0u; sampleNdx < numSamples; ++sampleNdx)
227         {
228             attachments[firstSubpassAttachmentsCount + sampleNdx] = attachmentRSDesc;
229         }
230     }
231 
232     const VkAttachmentReference attachmentMSColorRef = {
233         0u,                                      // uint32_t attachment;
234         VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL // VkImageLayout layout;
235     };
236 
237     const VkAttachmentReference attachmentMSInputRef = {
238         0u,                                      // uint32_t attachment;
239         VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL // VkImageLayout layout;
240     };
241 
242     const VkAttachmentReference attachmentRSColorRef = {
243         1u,                                      // uint32_t attachment;
244         VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL // VkImageLayout layout;
245     };
246 
247     std::vector<VkAttachmentReference> perSampleAttachmentRef(numSamples);
248 
249     for (uint32_t sampleNdx = 0u; sampleNdx < numSamples; ++sampleNdx)
250     {
251         const VkAttachmentReference attachmentRef = {
252             firstSubpassAttachmentsCount + sampleNdx, // uint32_t attachment;
253             VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL  // VkImageLayout layout;
254         };
255 
256         perSampleAttachmentRef[sampleNdx] = attachmentRef;
257     }
258 
259     std::vector<uint32_t> preserveAttachments(1u + numSamples);
260 
261     for (uint32_t attachNdx = 0u; attachNdx < 1u + numSamples; ++attachNdx)
262     {
263         preserveAttachments[attachNdx] = 1u + attachNdx;
264     }
265 
266     std::vector<VkSubpassDescription> subpasses(1u + numSamples);
267     std::vector<VkSubpassDependency> subpassDependencies;
268 
269     const VkSubpassDescription firstSubpassDesc = {
270         (VkSubpassDescriptionFlags)0u,   // VkSubpassDescriptionFlags flags;
271         VK_PIPELINE_BIND_POINT_GRAPHICS, // VkPipelineBindPoint pipelineBindPoint;
272         0u,                              // uint32_t inputAttachmentCount;
273         DE_NULL,                         // const VkAttachmentReference* pInputAttachments;
274         1u,                              // uint32_t colorAttachmentCount;
275         &attachmentMSColorRef,           // const VkAttachmentReference* pColorAttachments;
276         &attachmentRSColorRef,           // const VkAttachmentReference* pResolveAttachments;
277         DE_NULL,                         // const VkAttachmentReference* pDepthStencilAttachment;
278         0u,                              // uint32_t preserveAttachmentCount;
279         DE_NULL                          // const uint32_t* pPreserveAttachments;
280     };
281 
282     subpasses[0] = firstSubpassDesc;
283 
284     for (uint32_t sampleNdx = 0u; sampleNdx < numSamples; ++sampleNdx)
285     {
286         const VkSubpassDescription subpassDesc = {
287             (VkSubpassDescriptionFlags)0u,      // VkSubpassDescriptionFlags flags;
288             VK_PIPELINE_BIND_POINT_GRAPHICS,    // VkPipelineBindPoint pipelineBindPoint;
289             1u,                                 // uint32_t inputAttachmentCount;
290             &attachmentMSInputRef,              // const VkAttachmentReference* pInputAttachments;
291             1u,                                 // uint32_t colorAttachmentCount;
292             &perSampleAttachmentRef[sampleNdx], // const VkAttachmentReference* pColorAttachments;
293             DE_NULL,                            // const VkAttachmentReference* pResolveAttachments;
294             DE_NULL,                            // const VkAttachmentReference* pDepthStencilAttachment;
295             1u + sampleNdx,                     // uint32_t preserveAttachmentCount;
296             dataPointer(preserveAttachments)    // const uint32_t* pPreserveAttachments;
297         };
298 
299         subpasses[1u + sampleNdx] = subpassDesc;
300 
301         if (sampleNdx == 0u)
302         {
303             // The second subpass will be in charge of transitioning the multisample attachment from
304             // VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL.
305             const VkSubpassDependency subpassDependency = {
306                 0u,                                            // uint32_t                srcSubpass;
307                 1u + sampleNdx,                                // uint32_t                dstSubpass;
308                 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // VkPipelineStageFlags    srcStageMask;
309                 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,         // VkPipelineStageFlags    dstStageMask;
310                 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,          // VkAccessFlags           srcAccessMask;
311                 VK_ACCESS_INPUT_ATTACHMENT_READ_BIT,           // VkAccessFlags           dstAccessMask;
312                 0u,                                            // VkDependencyFlags       dependencyFlags;
313             };
314 
315             subpassDependencies.push_back(subpassDependency);
316         }
317         else
318         {
319             // Make sure subpass reads are in order. This serializes subpasses to make sure there are no layout transition hazards
320             // in the multisample image, from VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
321             // caused by parallel execution of several subpasses.
322             const VkSubpassDependency readDependency = {
323                 sampleNdx,                             // uint32_t                srcSubpass;
324                 1u + sampleNdx,                        // uint32_t                dstSubpass;
325                 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, // VkPipelineStageFlags    srcStageMask;
326                 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, // VkPipelineStageFlags    dstStageMask;
327                 VK_ACCESS_INPUT_ATTACHMENT_READ_BIT,   // VkAccessFlags           srcAccessMask;
328                 VK_ACCESS_INPUT_ATTACHMENT_READ_BIT,   // VkAccessFlags           dstAccessMask;
329                 0u,                                    // VkDependencyFlags       dependencyFlags;
330             };
331 
332             subpassDependencies.push_back(readDependency);
333         }
334     }
335 
336     const VkRenderPassCreateInfo renderPassInfo = {
337         VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,         // VkStructureType sType;
338         DE_NULL,                                           // const void* pNext;
339         (VkRenderPassCreateFlags)0u,                       // VkRenderPassCreateFlags flags;
340         static_cast<uint32_t>(attachments.size()),         // uint32_t attachmentCount;
341         dataPointer(attachments),                          // const VkAttachmentDescription* pAttachments;
342         static_cast<uint32_t>(subpasses.size()),           // uint32_t subpassCount;
343         dataPointer(subpasses),                            // const VkSubpassDescription* pSubpasses;
344         static_cast<uint32_t>(subpassDependencies.size()), // uint32_t dependencyCount;
345         dataPointer(subpassDependencies)                   // const VkSubpassDependency* pDependencies;
346     };
347 
348     RenderPassWrapper renderPass(m_imageMSParams.pipelineConstructionType, deviceInterface, device, &renderPassInfo);
349 
350     const VkImageSubresourceRange fullImageRange =
351         makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, imageMSInfo.mipLevels, 0u, imageMSInfo.arrayLayers);
352 
353     // Create color attachments image views
354     typedef de::SharedPtr<Unique<VkImageView>> VkImageViewSp;
355     std::vector<VkImageViewSp> imageViewsShPtrs(firstSubpassAttachmentsCount + numSamples);
356     std::vector<VkImage> images(firstSubpassAttachmentsCount + numSamples);
357     std::vector<VkImageView> imageViews(firstSubpassAttachmentsCount + numSamples);
358 
359     images[0] = **imageMS;
360     images[1] = **imageRS;
361 
362     imageViewsShPtrs[0] = makeVkSharedPtr(makeImageView(
363         deviceInterface, device, **imageMS, mapImageViewType(m_imageType), imageMSInfo.format, fullImageRange));
364     imageViewsShPtrs[1] = makeVkSharedPtr(makeImageView(
365         deviceInterface, device, **imageRS, mapImageViewType(m_imageType), imageRSInfo.format, fullImageRange));
366 
367     imageViews[0] = **imageViewsShPtrs[0];
368     imageViews[1] = **imageViewsShPtrs[1];
369 
370     for (uint32_t sampleNdx = 0u; sampleNdx < numSamples; ++sampleNdx)
371     {
372         images[firstSubpassAttachmentsCount + sampleNdx] = **imagesPerSampleVec[sampleNdx];
373         imageViewsShPtrs[firstSubpassAttachmentsCount + sampleNdx] =
374             makeVkSharedPtr(makeImageView(deviceInterface, device, **imagesPerSampleVec[sampleNdx],
375                                           mapImageViewType(m_imageType), imageRSInfo.format, fullImageRange));
376         imageViews[firstSubpassAttachmentsCount + sampleNdx] =
377             **imageViewsShPtrs[firstSubpassAttachmentsCount + sampleNdx];
378     }
379 
380     // Create framebuffer
381     const VkFramebufferCreateInfo framebufferInfo = {
382         VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, // VkStructureType    sType;
383         DE_NULL,                                   // const void*                                 pNext;
384         (VkFramebufferCreateFlags)0u,              // VkFramebufferCreateFlags                    flags;
385         *renderPass,                               // VkRenderPass                                renderPass;
386         static_cast<uint32_t>(imageViews.size()),  // uint32_t                                    attachmentCount;
387         dataPointer(imageViews),                   // const VkImageView*                          pAttachments;
388         imageMSInfo.extent.width,                  // uint32_t                                    width;
389         imageMSInfo.extent.height,                 // uint32_t                                    height;
390         imageMSInfo.arrayLayers,                   // uint32_t                                    layers;
391     };
392 
393     renderPass.createFramebuffer(deviceInterface, device, &framebufferInfo, images);
394 
395     const VkDescriptorSetLayout *descriptorSetLayoutMSPass = createMSPassDescSetLayout(m_imageMSParams);
396 
397     // Create pipeline layout
398     const VkPipelineLayoutCreateInfo pipelineLayoutMSPassParams = {
399         VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // VkStructureType sType;
400         DE_NULL,                                       // const void* pNext;
401         (VkPipelineLayoutCreateFlags)0u,               // VkPipelineLayoutCreateFlags flags;
402         descriptorSetLayoutMSPass ? 1u : 0u,           // uint32_t setLayoutCount;
403         descriptorSetLayoutMSPass,                     // const VkDescriptorSetLayout* pSetLayouts;
404         0u,                                            // uint32_t pushConstantRangeCount;
405         DE_NULL,                                       // const VkPushConstantRange* pPushConstantRanges;
406     };
407 
408     const PipelineLayoutWrapper pipelineLayoutMSPass(m_imageMSParams.pipelineConstructionType, deviceInterface, device,
409                                                      &pipelineLayoutMSPassParams);
410 
411     // Create vertex attributes data
412     const VertexDataDesc vertexDataDesc = getVertexDataDescripton();
413 
414     de::SharedPtr<BufferWithMemory> vertexBuffer = de::SharedPtr<BufferWithMemory>(
415         new BufferWithMemory(deviceInterface, device, allocator,
416                              makeBufferCreateInfo(vertexDataDesc.dataSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT),
417                              MemoryRequirement::HostVisible));
418     const Allocation &vertexBufferAllocation = vertexBuffer->getAllocation();
419 
420     uploadVertexData(vertexBufferAllocation, vertexDataDesc);
421 
422     flushAlloc(deviceInterface, device, vertexBufferAllocation);
423 
424     const VkVertexInputBindingDescription vertexBinding = {
425         0u,                         // uint32_t binding;
426         vertexDataDesc.dataStride,  // uint32_t stride;
427         VK_VERTEX_INPUT_RATE_VERTEX // VkVertexInputRate inputRate;
428     };
429 
430     const VkPipelineVertexInputStateCreateInfo vertexInputStateInfo = {
431         VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, // VkStructureType                             sType;
432         DE_NULL,                                                   // const void*                                 pNext;
433         (VkPipelineVertexInputStateCreateFlags)0u,                 // VkPipelineVertexInputStateCreateFlags       flags;
434         1u,             // uint32_t                                    vertexBindingDescriptionCount;
435         &vertexBinding, // const VkVertexInputBindingDescription*      pVertexBindingDescriptions;
436         static_cast<uint32_t>(
437             vertexDataDesc.vertexAttribDescVec
438                 .size()), // uint32_t                                    vertexAttributeDescriptionCount;
439         dataPointer(
440             vertexDataDesc
441                 .vertexAttribDescVec), // const VkVertexInputAttributeDescription*    pVertexAttributeDescriptions;
442     };
443 
444     const std::vector<VkViewport> viewports{makeViewport(imageMSInfo.extent)};
445     const std::vector<VkRect2D> scissors{makeRect2D(imageMSInfo.extent)};
446 
447     const VkPipelineMultisampleStateCreateInfo multisampleStateInfo = getMSStateCreateInfo(m_imageMSParams);
448 
449     // Create graphics pipeline for multisample pass
450     const ShaderWrapper vsMSPassModule(ShaderWrapper(
451         deviceInterface, device, m_context.getBinaryCollection().get("vertex_shader"), (VkShaderModuleCreateFlags)0u));
452     const ShaderWrapper fsMSPassModule(ShaderWrapper(deviceInterface, device,
453                                                      m_context.getBinaryCollection().get("fragment_shader"),
454                                                      (VkShaderModuleCreateFlags)0u));
455 
456     GraphicsPipelineWrapper graphicsPipelineMSPass(instance, deviceInterface, physicalDevice, device,
457                                                    m_context.getDeviceExtensions(),
458                                                    m_imageMSParams.pipelineConstructionType);
459     graphicsPipelineMSPass.setDefaultColorBlendState()
460         .setDefaultDepthStencilState()
461         .setDefaultRasterizationState()
462         .setDefaultTopology(vertexDataDesc.primitiveTopology)
463         .setupVertexInputState(&vertexInputStateInfo)
464         .setupPreRasterizationShaderState(viewports, scissors, pipelineLayoutMSPass, *renderPass, 0u, vsMSPassModule)
465         .setupFragmentShaderState(pipelineLayoutMSPass, *renderPass, 0u, fsMSPassModule, DE_NULL, &multisampleStateInfo)
466         .setupFragmentOutputState(*renderPass, 0u, DE_NULL, &multisampleStateInfo)
467         .setMonolithicPipelineLayout(pipelineLayoutMSPass)
468         .buildPipeline();
469 
470     std::vector<GraphicsPipelineWrapper> graphicsPipelinesPerSampleFetch;
471     graphicsPipelinesPerSampleFetch.reserve(numSamples);
472 
473     // Create descriptor set layout
474     const Unique<VkDescriptorSetLayout> descriptorSetLayout(
475         DescriptorSetLayoutBuilder()
476             .addSingleBinding(VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, VK_SHADER_STAGE_FRAGMENT_BIT)
477             .addSingleBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, VK_SHADER_STAGE_FRAGMENT_BIT)
478             .build(deviceInterface, device));
479 
480     const PipelineLayoutWrapper pipelineLayoutPerSampleFetchPass(m_imageMSParams.pipelineConstructionType,
481                                                                  deviceInterface, device, *descriptorSetLayout);
482 
483     const uint32_t bufferPerSampleFetchPassSize = 4u * (uint32_t)sizeof(tcu::Vec4);
484 
485     de::SharedPtr<BufferWithMemory> vertexBufferPerSampleFetchPass = de::SharedPtr<BufferWithMemory>(
486         new BufferWithMemory(deviceInterface, device, allocator,
487                              makeBufferCreateInfo(bufferPerSampleFetchPassSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT),
488                              MemoryRequirement::HostVisible));
489 
490     // Create graphics pipelines for per sample texel fetch passes
491     {
492         const ShaderWrapper vsPerSampleFetchPassModule(
493             ShaderWrapper(deviceInterface, device, m_context.getBinaryCollection().get("per_sample_fetch_vs"),
494                           (VkShaderModuleCreateFlags)0u));
495         const ShaderWrapper fsPerSampleFetchPassModule(
496             ShaderWrapper(deviceInterface, device, m_context.getBinaryCollection().get("per_sample_fetch_fs"),
497                           (VkShaderModuleCreateFlags)0u));
498 
499         std::vector<tcu::Vec4> vertices;
500 
501         vertices.push_back(tcu::Vec4(-1.0f, -1.0f, 0.0f, 1.0f));
502         vertices.push_back(tcu::Vec4(1.0f, -1.0f, 0.0f, 1.0f));
503         vertices.push_back(tcu::Vec4(-1.0f, 1.0f, 0.0f, 1.0f));
504         vertices.push_back(tcu::Vec4(1.0f, 1.0f, 0.0f, 1.0f));
505 
506         const Allocation &vertexAllocPerSampleFetchPass = vertexBufferPerSampleFetchPass->getAllocation();
507 
508         deMemcpy(vertexAllocPerSampleFetchPass.getHostPtr(), dataPointer(vertices),
509                  static_cast<std::size_t>(bufferPerSampleFetchPassSize));
510 
511         flushAlloc(deviceInterface, device, vertexAllocPerSampleFetchPass);
512 
513         for (uint32_t sampleNdx = 0u; sampleNdx < numSamples; ++sampleNdx)
514         {
515             const uint32_t subpass = 1u + sampleNdx;
516             graphicsPipelinesPerSampleFetch.emplace_back(instance, deviceInterface, physicalDevice, device,
517                                                          m_context.getDeviceExtensions(),
518                                                          m_imageMSParams.pipelineConstructionType);
519             graphicsPipelinesPerSampleFetch.back()
520                 .setDefaultMultisampleState()
521                 .setDefaultColorBlendState()
522                 .setDefaultDepthStencilState()
523                 .setDefaultRasterizationState()
524                 .setDefaultTopology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP)
525                 .setupVertexInputState()
526                 .setupPreRasterizationShaderState(viewports, scissors, pipelineLayoutPerSampleFetchPass, *renderPass,
527                                                   subpass, vsPerSampleFetchPassModule)
528                 .setupFragmentShaderState(pipelineLayoutPerSampleFetchPass, *renderPass, subpass,
529                                           fsPerSampleFetchPassModule)
530                 .setupFragmentOutputState(*renderPass, subpass)
531                 .setMonolithicPipelineLayout(pipelineLayoutPerSampleFetchPass)
532                 .buildPipeline();
533         }
534     }
535 
536     // Create descriptor pool
537     const Unique<VkDescriptorPool> descriptorPool(
538         DescriptorPoolBuilder()
539             .addType(VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1u)
540             .addType(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1u)
541             .build(deviceInterface, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
542 
543     // Create descriptor set
544     const Unique<VkDescriptorSet> descriptorSet(
545         makeDescriptorSet(deviceInterface, device, *descriptorPool, *descriptorSetLayout));
546 
547     const VkPhysicalDeviceLimits deviceLimits = getPhysicalDeviceProperties(instance, physicalDevice).limits;
548 
549     VkDeviceSize uboOffsetAlignment = sizeof(int32_t) < deviceLimits.minUniformBufferOffsetAlignment ?
550                                           deviceLimits.minUniformBufferOffsetAlignment :
551                                           sizeof(int32_t);
552 
553     uboOffsetAlignment += (deviceLimits.minUniformBufferOffsetAlignment -
554                            uboOffsetAlignment % deviceLimits.minUniformBufferOffsetAlignment) %
555                           deviceLimits.minUniformBufferOffsetAlignment;
556 
557     const VkBufferCreateInfo bufferSampleIDInfo =
558         makeBufferCreateInfo(uboOffsetAlignment * numSamples, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT);
559     const de::UniquePtr<BufferWithMemory> bufferSampleID(
560         new BufferWithMemory(deviceInterface, device, allocator, bufferSampleIDInfo, MemoryRequirement::HostVisible));
561 
562     std::vector<uint32_t> sampleIDsOffsets(numSamples);
563 
564     {
565         int8_t *sampleIDs = new int8_t[static_cast<uint32_t>(uboOffsetAlignment) * numSamples];
566 
567         for (int32_t sampleNdx = 0u; sampleNdx < static_cast<int32_t>(numSamples); ++sampleNdx)
568         {
569             sampleIDsOffsets[sampleNdx] = static_cast<uint32_t>(sampleNdx * uboOffsetAlignment);
570             int8_t *samplePtr           = sampleIDs + sampleIDsOffsets[sampleNdx];
571 
572             deMemcpy(samplePtr, &sampleNdx, sizeof(int32_t));
573         }
574 
575         deMemcpy(bufferSampleID->getAllocation().getHostPtr(), sampleIDs,
576                  static_cast<uint32_t>(uboOffsetAlignment * numSamples));
577 
578         flushAlloc(deviceInterface, device, bufferSampleID->getAllocation());
579 
580         delete[] sampleIDs;
581     }
582 
583     {
584         const VkDescriptorImageInfo descImageInfo =
585             makeDescriptorImageInfo(DE_NULL, imageViews[0], VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
586         const VkDescriptorBufferInfo descBufferInfo = makeDescriptorBufferInfo(**bufferSampleID, 0u, sizeof(int32_t));
587 
588         DescriptorSetUpdateBuilder()
589             .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u),
590                          VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, &descImageInfo)
591             .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(1u),
592                          VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, &descBufferInfo)
593             .update(deviceInterface, device);
594     }
595 
596     // Create command buffer for compute and transfer oparations
597     const Unique<VkCommandPool> commandPool(
598         createCommandPool(deviceInterface, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex));
599     const Unique<VkCommandBuffer> commandBuffer(makeCommandBuffer(deviceInterface, device, *commandPool));
600 
601     // Start recording commands
602     beginCommandBuffer(deviceInterface, *commandBuffer);
603 
604     {
605         std::vector<VkImageMemoryBarrier> imageOutputAttachmentBarriers(firstSubpassAttachmentsCount + numSamples);
606 
607         imageOutputAttachmentBarriers[0] =
608             makeImageMemoryBarrier(0u, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED,
609                                    VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, **imageMS, fullImageRange);
610 
611         imageOutputAttachmentBarriers[1] =
612             makeImageMemoryBarrier(0u, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED,
613                                    VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, **imageRS, fullImageRange);
614 
615         for (uint32_t sampleNdx = 0u; sampleNdx < numSamples; ++sampleNdx)
616         {
617             imageOutputAttachmentBarriers[firstSubpassAttachmentsCount + sampleNdx] = makeImageMemoryBarrier(
618                 0u, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED,
619                 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, **imagesPerSampleVec[sampleNdx], fullImageRange);
620         }
621 
622         deviceInterface.cmdPipelineBarrier(*commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
623                                            VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0u, 0u, DE_NULL, 0u, DE_NULL,
624                                            static_cast<uint32_t>(imageOutputAttachmentBarriers.size()),
625                                            dataPointer(imageOutputAttachmentBarriers));
626     }
627 
628     {
629         const VkDeviceSize vertexStartOffset = 0u;
630 
631         std::vector<VkClearValue> clearValues(firstSubpassAttachmentsCount + numSamples);
632         for (uint32_t attachmentNdx = 0u; attachmentNdx < firstSubpassAttachmentsCount + numSamples; ++attachmentNdx)
633         {
634             clearValues[attachmentNdx] = makeClearValueColor(tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
635         }
636 
637         renderPass.begin(deviceInterface, *commandBuffer,
638                          makeRect2D(0, 0, imageMSInfo.extent.width, imageMSInfo.extent.height),
639                          (uint32_t)clearValues.size(), dataPointer(clearValues));
640 
641         // Bind graphics pipeline
642         graphicsPipelineMSPass.bind(*commandBuffer);
643 
644         const VkDescriptorSet *descriptorSetMSPass = createMSPassDescSet(m_imageMSParams, descriptorSetLayoutMSPass);
645 
646         if (descriptorSetMSPass)
647         {
648             // Bind descriptor set
649             deviceInterface.cmdBindDescriptorSets(*commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
650                                                   *pipelineLayoutMSPass, 0u, 1u, descriptorSetMSPass, 0u, DE_NULL);
651         }
652 
653         // Bind vertex buffer
654         deviceInterface.cmdBindVertexBuffers(*commandBuffer, 0u, 1u, &vertexBuffer->get(), &vertexStartOffset);
655 
656         // Perform a draw
657         deviceInterface.cmdDraw(*commandBuffer, vertexDataDesc.verticesCount, 1u, 0u, 0u);
658 
659         for (uint32_t sampleNdx = 0u; sampleNdx < numSamples; ++sampleNdx)
660         {
661             renderPass.nextSubpass(deviceInterface, *commandBuffer, VK_SUBPASS_CONTENTS_INLINE);
662 
663             // Bind graphics pipeline
664             graphicsPipelinesPerSampleFetch[sampleNdx].bind(*commandBuffer);
665 
666             // Bind descriptor set
667             deviceInterface.cmdBindDescriptorSets(*commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
668                                                   *pipelineLayoutPerSampleFetchPass, 0u, 1u, &descriptorSet.get(), 1u,
669                                                   &sampleIDsOffsets[sampleNdx]);
670 
671             // Bind vertex buffer
672             deviceInterface.cmdBindVertexBuffers(*commandBuffer, 0u, 1u, &vertexBufferPerSampleFetchPass->get(),
673                                                  &vertexStartOffset);
674 
675             // Perform a draw
676             deviceInterface.cmdDraw(*commandBuffer, 4u, 1u, 0u, 0u);
677         }
678 
679         // End render pass
680         renderPass.end(deviceInterface, *commandBuffer);
681     }
682 
683     {
684         const VkImageMemoryBarrier imageRSTransferBarrier = makeImageMemoryBarrier(
685             VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
686             VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, **imageRS, fullImageRange);
687 
688         deviceInterface.cmdPipelineBarrier(*commandBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
689                                            VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 0u, DE_NULL, 0u, DE_NULL, 1u,
690                                            &imageRSTransferBarrier);
691     }
692 
693     // Copy data from imageRS to buffer
694     const uint32_t imageRSSizeInBytes =
695         getImageSizeInBytes(imageRSInfo.extent, imageRSInfo.arrayLayers, m_imageFormat, imageRSInfo.mipLevels, 1u);
696 
697     const VkBufferCreateInfo bufferRSInfo = makeBufferCreateInfo(imageRSSizeInBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT);
698     const de::UniquePtr<BufferWithMemory> bufferRS(
699         new BufferWithMemory(deviceInterface, device, allocator, bufferRSInfo, MemoryRequirement::HostVisible));
700 
701     {
702         const VkBufferImageCopy bufferImageCopy = {
703             0u, // VkDeviceSize bufferOffset;
704             0u, // uint32_t bufferRowLength;
705             0u, // uint32_t bufferImageHeight;
706             makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u,
707                                        imageRSInfo.arrayLayers), // VkImageSubresourceLayers imageSubresource;
708             makeOffset3D(0, 0, 0),                               // VkOffset3D imageOffset;
709             imageRSInfo.extent,                                  // VkExtent3D imageExtent;
710         };
711 
712         deviceInterface.cmdCopyImageToBuffer(*commandBuffer, **imageRS, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
713                                              bufferRS->get(), 1u, &bufferImageCopy);
714     }
715 
716     {
717         const VkBufferMemoryBarrier bufferRSHostReadBarrier = makeBufferMemoryBarrier(
718             VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, bufferRS->get(), 0u, imageRSSizeInBytes);
719 
720         deviceInterface.cmdPipelineBarrier(*commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT,
721                                            0u, 0u, DE_NULL, 1u, &bufferRSHostReadBarrier, 0u, DE_NULL);
722     }
723 
724     // Copy data from per sample images to buffers
725     std::vector<VkImageMemoryBarrier> imagesPerSampleTransferBarriers(numSamples);
726 
727     for (uint32_t sampleNdx = 0u; sampleNdx < numSamples; ++sampleNdx)
728     {
729         imagesPerSampleTransferBarriers[sampleNdx] = makeImageMemoryBarrier(
730             VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
731             VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, **imagesPerSampleVec[sampleNdx], fullImageRange);
732     }
733 
734     deviceInterface.cmdPipelineBarrier(*commandBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
735                                        VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 0u, DE_NULL, 0u, DE_NULL,
736                                        static_cast<uint32_t>(imagesPerSampleTransferBarriers.size()),
737                                        dataPointer(imagesPerSampleTransferBarriers));
738 
739     std::vector<de::SharedPtr<BufferWithMemory>> buffersPerSample(numSamples);
740 
741     for (uint32_t sampleNdx = 0u; sampleNdx < numSamples; ++sampleNdx)
742     {
743         buffersPerSample[sampleNdx] = de::SharedPtr<BufferWithMemory>(
744             new BufferWithMemory(deviceInterface, device, allocator, bufferRSInfo, MemoryRequirement::HostVisible));
745 
746         const VkBufferImageCopy bufferImageCopy = {
747             0u, // VkDeviceSize bufferOffset;
748             0u, // uint32_t bufferRowLength;
749             0u, // uint32_t bufferImageHeight;
750             makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u,
751                                        imageRSInfo.arrayLayers), // VkImageSubresourceLayers imageSubresource;
752             makeOffset3D(0, 0, 0),                               // VkOffset3D imageOffset;
753             imageRSInfo.extent,                                  // VkExtent3D imageExtent;
754         };
755 
756         deviceInterface.cmdCopyImageToBuffer(*commandBuffer, **imagesPerSampleVec[sampleNdx],
757                                              VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, **buffersPerSample[sampleNdx], 1u,
758                                              &bufferImageCopy);
759     }
760 
761     std::vector<VkBufferMemoryBarrier> buffersPerSampleHostReadBarriers(numSamples);
762 
763     for (uint32_t sampleNdx = 0u; sampleNdx < numSamples; ++sampleNdx)
764     {
765         buffersPerSampleHostReadBarriers[sampleNdx] =
766             makeBufferMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT,
767                                     **buffersPerSample[sampleNdx], 0u, imageRSSizeInBytes);
768     }
769 
770     deviceInterface.cmdPipelineBarrier(*commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
771                                        0u, DE_NULL, static_cast<uint32_t>(buffersPerSampleHostReadBarriers.size()),
772                                        dataPointer(buffersPerSampleHostReadBarriers), 0u, DE_NULL);
773 
774     // End recording commands
775     endCommandBuffer(deviceInterface, *commandBuffer);
776 
777     // Submit commands for execution and wait for completion
778     submitCommandsAndWait(deviceInterface, device, queue, *commandBuffer);
779 
780     // Retrieve data from bufferRS to host memory
781     const Allocation &bufferRSAlloc = bufferRS->getAllocation();
782 
783     invalidateAlloc(deviceInterface, device, bufferRSAlloc);
784 
785     const tcu::ConstPixelBufferAccess bufferRSData(m_imageFormat, imageRSInfo.extent.width, imageRSInfo.extent.height,
786                                                    imageRSInfo.extent.depth * imageRSInfo.arrayLayers,
787                                                    bufferRSAlloc.getHostPtr());
788 
789     std::stringstream resolveName;
790     resolveName << "Resolve image " << getImageTypeName(m_imageType) << "_" << bufferRSData.getWidth() << "_"
791                 << bufferRSData.getHeight() << "_" << bufferRSData.getDepth() << std::endl;
792 
793     m_context.getTestContext().getLog() << tcu::TestLog::Section(resolveName.str(), resolveName.str())
794                                         << tcu::LogImage("resolve", "", bufferRSData) << tcu::TestLog::EndSection;
795 
796     std::vector<tcu::ConstPixelBufferAccess> buffersPerSampleData(numSamples);
797 
798     // Retrieve data from per sample buffers to host memory
799     for (uint32_t sampleNdx = 0u; sampleNdx < numSamples; ++sampleNdx)
800     {
801         const Allocation &bufferAlloc = buffersPerSample[sampleNdx]->getAllocation();
802 
803         invalidateAlloc(deviceInterface, device, bufferAlloc);
804 
805         buffersPerSampleData[sampleNdx] =
806             tcu::ConstPixelBufferAccess(m_imageFormat, imageRSInfo.extent.width, imageRSInfo.extent.height,
807                                         imageRSInfo.extent.depth * imageRSInfo.arrayLayers, bufferAlloc.getHostPtr());
808 
809         std::stringstream sampleName;
810         sampleName << "Sample " << sampleNdx << " image" << std::endl;
811 
812         m_context.getTestContext().getLog()
813             << tcu::TestLog::Section(sampleName.str(), sampleName.str())
814             << tcu::LogImage("sample", "", buffersPerSampleData[sampleNdx]) << tcu::TestLog::EndSection;
815     }
816 
817     return verifyImageData(imageMSInfo, imageRSInfo, buffersPerSampleData, bufferRSData);
818 }
819 
820 } // namespace multisample
821 } // namespace pipeline
822 } // namespace vkt
823