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