• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*-------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2017 Google Inc.
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  *//*!
20  * \file
21  * \brief Testing compute shader writing to separate planes of a multiplanar format
22  *//*--------------------------------------------------------------------*/
23 
24 #include "vktYCbCrStorageImageWriteTests.hpp"
25 #include "vktTestCaseUtil.hpp"
26 #include "vktTestGroupUtil.hpp"
27 #include "vktYCbCrUtil.hpp"
28 #include "vkBuilderUtil.hpp"
29 #include "vkObjUtil.hpp"
30 #include "vkCmdUtil.hpp"
31 #include "vkBarrierUtil.hpp"
32 #include "vkImageUtil.hpp"
33 #include "tcuTexVerifierUtil.hpp"
34 #include "vkTypeUtil.hpp"
35 #include "vkRefUtil.hpp"
36 #include "vkQueryUtil.hpp"
37 #include "tcuTestLog.hpp"
38 
39 namespace vkt
40 {
41 namespace ycbcr
42 {
43 namespace
44 {
45 
46 using namespace vk;
47 
48 struct TestParameters
49 {
50 	VkFormat			format;
51 	tcu::UVec3			size;
52 	VkImageCreateFlags	flags;
53 
TestParametersvkt::ycbcr::__anon1aedeedf0111::TestParameters54 	TestParameters (VkFormat			format_,
55 					const tcu::UVec3&	size_,
56 					VkImageCreateFlags	flags_)
57 		: format			(format_)
58 		, size				(size_)
59 		, flags				(flags_)
60 	{
61 	}
62 
TestParametersvkt::ycbcr::__anon1aedeedf0111::TestParameters63 	TestParameters (void)
64 		: format			(VK_FORMAT_UNDEFINED)
65 		, flags				(0u)
66 	{
67 	}
68 };
69 
checkSupport(Context & context,const TestParameters params)70 void checkSupport (Context& context, const TestParameters params)
71 {
72 	const bool							disjoint = (params.flags & VK_IMAGE_CREATE_DISJOINT_BIT) != 0;
73 	std::vector<std::string>			reqExts;
74 
75 	if (disjoint)
76 	{
77 		if (!isCoreDeviceExtension(context.getUsedApiVersion(), "VK_KHR_bind_memory2"))
78 			reqExts.push_back("VK_KHR_bind_memory2");
79 		if (!isCoreDeviceExtension(context.getUsedApiVersion(), "VK_KHR_get_memory_requirements2"))
80 			reqExts.push_back("VK_KHR_get_memory_requirements2");
81 	}
82 
83 	for ( const auto& extIter : reqExts )
84 	{
85 		if (!context.isDeviceFunctionalitySupported(extIter))
86 			TCU_THROW(NotSupportedError, (extIter + " is not supported").c_str());
87 	}
88 
89 	{
90 		const VkFormatProperties	formatProperties = getPhysicalDeviceFormatProperties(context.getInstanceInterface(),
91 			context.getPhysicalDevice(),
92 			params.format);
93 
94 		if ((formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT) == 0)
95 			TCU_THROW(NotSupportedError, "Storage images are not supported for this format");
96 
97 		if (disjoint && ((formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_DISJOINT_BIT) == 0))
98 			TCU_THROW(NotSupportedError, "Disjoint planes are not supported for this format");
99 	}
100 }
101 
102 template<typename T>
makeVkSharedPtr(vk::Move<T> vkMove)103 inline de::SharedPtr<vk::Unique<T> > makeVkSharedPtr(vk::Move<T> vkMove)
104 {
105 	return de::SharedPtr<vk::Unique<T> >(new vk::Unique<T>(vkMove));
106 }
107 
computeWorkGroupSize(const VkExtent3D & planeExtent)108 tcu::UVec3 computeWorkGroupSize(const VkExtent3D& planeExtent)
109 {
110 	const deUint32		maxComputeWorkGroupInvocations	= 128u;
111 	const tcu::UVec3	maxComputeWorkGroupSize			= tcu::UVec3(128u, 128u, 64u);
112 
113 	const deUint32		xWorkGroupSize					= std::min(std::min(planeExtent.width, maxComputeWorkGroupSize.x()), maxComputeWorkGroupInvocations);
114 	const deUint32		yWorkGroupSize					= std::min(std::min(planeExtent.height, maxComputeWorkGroupSize.y()), maxComputeWorkGroupInvocations / xWorkGroupSize);
115 	const deUint32		zWorkGroupSize					= std::min(std::min(planeExtent.depth, maxComputeWorkGroupSize.z()), maxComputeWorkGroupInvocations / (xWorkGroupSize*yWorkGroupSize));
116 
117 	return tcu::UVec3(xWorkGroupSize, yWorkGroupSize, zWorkGroupSize);
118 }
119 
makeComputePipeline(const DeviceInterface & vk,const VkDevice device,const VkPipelineLayout pipelineLayout,const VkShaderModule shaderModule,const VkSpecializationInfo * specializationInfo)120 Move<VkPipeline> makeComputePipeline (const DeviceInterface&		vk,
121 									  const VkDevice				device,
122 									  const VkPipelineLayout		pipelineLayout,
123 									  const VkShaderModule			shaderModule,
124 									  const VkSpecializationInfo*	specializationInfo)
125 {
126 	const VkPipelineShaderStageCreateInfo pipelineShaderStageParams =
127 	{
128 		VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,	// VkStructureType						sType;
129 		DE_NULL,												// const void*							pNext;
130 		0u,														// VkPipelineShaderStageCreateFlags		flags;
131 		VK_SHADER_STAGE_COMPUTE_BIT,							// VkShaderStageFlagBits				stage;
132 		shaderModule,											// VkShaderModule						module;
133 		"main",													// const char*							pName;
134 		specializationInfo,										// const VkSpecializationInfo*			pSpecializationInfo;
135 	};
136 	const VkComputePipelineCreateInfo pipelineCreateInfo =
137 	{
138 		VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,		// VkStructureType					sType;
139 		DE_NULL,											// const void*						pNext;
140 		0u,													// VkPipelineCreateFlags			flags;
141 		pipelineShaderStageParams,							// VkPipelineShaderStageCreateInfo	stage;
142 		pipelineLayout,										// VkPipelineLayout					layout;
143 		DE_NULL,											// VkPipeline						basePipelineHandle;
144 		0,													// deInt32							basePipelineIndex;
145 	};
146 	return createComputePipeline(vk, device, DE_NULL , &pipelineCreateInfo);
147 }
148 
getPlaneCompatibleFormatForWriting(const vk::PlanarFormatDescription & formatInfo,deUint32 planeNdx)149 vk::VkFormat getPlaneCompatibleFormatForWriting(const vk::PlanarFormatDescription& formatInfo, deUint32 planeNdx)
150 {
151 	DE_ASSERT(planeNdx < formatInfo.numPlanes);
152 	vk::VkFormat result = formatInfo.planes[planeNdx].planeCompatibleFormat;
153 
154 	// redirect result for some of the YCbCr image formats
155 	static const std::pair<vk::VkFormat, vk::VkFormat> ycbcrFormats[] =
156 	{
157 		{ VK_FORMAT_G8B8G8R8_422_UNORM_KHR,						VK_FORMAT_R8G8B8A8_UNORM		},
158 		{ VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16_KHR,	VK_FORMAT_R16G16B16A16_UNORM	},
159 		{ VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16_KHR,	VK_FORMAT_R16G16B16A16_UNORM	},
160 		{ VK_FORMAT_G16B16G16R16_422_UNORM_KHR,					VK_FORMAT_R16G16B16A16_UNORM	},
161 		{ VK_FORMAT_B8G8R8G8_422_UNORM_KHR,						VK_FORMAT_R8G8B8A8_UNORM		},
162 		{ VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16_KHR,	VK_FORMAT_R16G16B16A16_UNORM	},
163 		{ VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16_KHR,	VK_FORMAT_R16G16B16A16_UNORM	},
164 		{ VK_FORMAT_B16G16R16G16_422_UNORM_KHR,					VK_FORMAT_R16G16B16A16_UNORM	}
165 	};
166 	auto it = std::find_if(std::begin(ycbcrFormats), std::end(ycbcrFormats), [result](const std::pair<vk::VkFormat, vk::VkFormat>& p) { return p.first == result; });
167 	if (it != std::end(ycbcrFormats))
168 		result = it->second;
169 	return result;
170 }
171 
testStorageImageWrite(Context & context,TestParameters params)172 tcu::TestStatus testStorageImageWrite (Context& context, TestParameters params)
173 {
174 	const DeviceInterface&						vkd						= context.getDeviceInterface();
175 	const VkDevice								device					= context.getDevice();
176 	const deUint32								queueFamilyIndex		= context.getUniversalQueueFamilyIndex();
177 	const VkQueue								queue					= context.getUniversalQueue();
178 	const PlanarFormatDescription				formatDescription		= getPlanarFormatDescription(params.format);
179 
180 	VkImageCreateInfo							imageCreateInfo =
181 	{
182 		VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
183 		DE_NULL,
184 		params.flags,
185 		VK_IMAGE_TYPE_2D,
186 		params.format,
187 		makeExtent3D(params.size.x(), params.size.y(), params.size.z()),
188 		1u,			// mipLevels
189 		1u,			// arrayLayers
190 		VK_SAMPLE_COUNT_1_BIT,
191 		VK_IMAGE_TILING_OPTIMAL,
192 		VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_STORAGE_BIT,
193 		VK_SHARING_MODE_EXCLUSIVE,
194 		0u,
195 		(const deUint32*)DE_NULL,
196 		VK_IMAGE_LAYOUT_UNDEFINED,
197 	};
198 
199 	// check if we need to create VkImageView with different VkFormat than VkImage format
200 	VkFormat planeCompatibleFormat0 = getPlaneCompatibleFormatForWriting(formatDescription, 0);
201 	if (planeCompatibleFormat0 != getPlaneCompatibleFormat(formatDescription, 0))
202 	{
203 		imageCreateInfo.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
204 	}
205 
206 	const Unique<VkImage>						image					(createImage(vkd, device, &imageCreateInfo));
207 	// allocate memory for the whole image, or for each separate plane ( if the params.flags include VK_IMAGE_CREATE_DISJOINT_BIT )
208 	const std::vector<AllocationSp>				allocations				(allocateAndBindImageMemory(vkd, device, context.getDefaultAllocator(), *image, params.format, params.flags, MemoryRequirement::Any));
209 
210 	// Create descriptor set layout
211 	const Unique<VkDescriptorSetLayout>			descriptorSetLayout		(DescriptorSetLayoutBuilder()
212 		.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT)
213 		.build(vkd, device));
214 	const Unique<VkPipelineLayout>				pipelineLayout			(makePipelineLayout(vkd, device, *descriptorSetLayout));
215 
216 	// Create descriptor sets
217 	const Unique<VkDescriptorPool>				descriptorPool			(DescriptorPoolBuilder()
218 		.addType(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1u)
219 		.build(vkd, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, vk::PlanarFormatDescription::MAX_PLANES));
220 
221 	// Create command buffer for compute and transfer operations
222 	const Unique<VkCommandPool>					commandPool				(makeCommandPool(vkd, device, queueFamilyIndex));
223 	const Unique<VkCommandBuffer>				commandBuffer			(allocateCommandBuffer(vkd, device, *commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
224 
225 	std::vector<de::SharedPtr<vk::Unique<vk::VkShaderModule>>>			shaderModules;
226 	std::vector<de::SharedPtr<vk::Unique<vk::VkPipeline>>>				computePipelines;
227 	std::vector<de::SharedPtr<vk::Unique<vk::VkDescriptorSet>>>			descriptorSets;
228 	std::vector<de::SharedPtr<vk::Unique<vk::VkImageView>>>				imageViews;
229 
230 	deUint32									imageSizeInBytes		= 0;
231 	deUint32									planeOffsets[PlanarFormatDescription::MAX_PLANES];
232 	deUint32									planeRowPitches[PlanarFormatDescription::MAX_PLANES];
233 	void*										planePointers[PlanarFormatDescription::MAX_PLANES];
234 
235 	{
236 		// Start recording commands
237 		beginCommandBuffer(vkd, *commandBuffer);
238 
239 		for (deUint32 planeNdx = 0; planeNdx < formatDescription.numPlanes; ++planeNdx)
240 		{
241 			const VkImageAspectFlags		aspect						= (formatDescription.numPlanes > 1) ? getPlaneAspect(planeNdx) : VK_IMAGE_ASPECT_COLOR_BIT;
242 			const VkImageSubresourceRange	subresourceRange			= makeImageSubresourceRange(aspect, 0u, 1u, 0u, 1u);
243 			VkFormat						planeCompatibleFormat		= getPlaneCompatibleFormatForWriting(formatDescription, planeNdx);
244 			vk::PlanarFormatDescription		compatibleFormatDescription = (planeCompatibleFormat != getPlaneCompatibleFormat(formatDescription, planeNdx)) ? getPlanarFormatDescription(planeCompatibleFormat) : formatDescription;
245 			const tcu::UVec3				compatibleShaderGridSize	( params.size.x() / formatDescription.blockWidth, params.size.y() / formatDescription.blockHeight, params.size.z() / 1u);
246 			VkExtent3D						shaderExtent				= getPlaneExtent(compatibleFormatDescription, VkExtent3D{ compatibleShaderGridSize.x(), compatibleShaderGridSize.y(), compatibleShaderGridSize.z() }, planeNdx, 0u);
247 
248 			// Create and bind compute pipeline
249 			std::ostringstream shaderName;
250 			shaderName << "comp" << planeNdx;
251 			auto							shaderModule			= makeVkSharedPtr(createShaderModule(vkd, device, context.getBinaryCollection().get(shaderName.str()), DE_NULL));
252 			shaderModules.push_back(shaderModule);
253 			auto							computePipeline			= makeVkSharedPtr(makeComputePipeline(vkd, device, *pipelineLayout, shaderModule->get(), DE_NULL));
254 			computePipelines.push_back(computePipeline);
255 			vkd.cmdBindPipeline(*commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipeline->get());
256 
257 			auto							descriptorSet			= makeVkSharedPtr(makeDescriptorSet(vkd, device, *descriptorPool, *descriptorSetLayout));
258 			descriptorSets.push_back(descriptorSet);
259 
260 			auto							imageView				= makeVkSharedPtr(makeImageView(vkd, device, *image, VK_IMAGE_VIEW_TYPE_2D, planeCompatibleFormat, subresourceRange));
261 			imageViews.push_back(imageView);
262 			const VkDescriptorImageInfo		imageInfo				= makeDescriptorImageInfo(DE_NULL, imageView->get(), VK_IMAGE_LAYOUT_GENERAL);
263 
264 			DescriptorSetUpdateBuilder()
265 				.writeSingle(descriptorSet->get(), DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &imageInfo)
266 				.update(vkd, device);
267 
268 			vkd.cmdBindDescriptorSets(*commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0u, 1u, &descriptorSet->get(), 0u, DE_NULL);
269 
270 			{
271 				const VkImageMemoryBarrier imageLayoutChangeBarrier = makeImageMemoryBarrier(0u, VK_ACCESS_SHADER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, *image, subresourceRange, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED);
272 				vkd.cmdPipelineBarrier(*commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0u, 0u, DE_NULL, 0u, DE_NULL, 1u, &imageLayoutChangeBarrier);
273 			}
274 
275 			{
276 				const tcu::UVec3 workGroupSize = computeWorkGroupSize(shaderExtent);
277 
278 				const deUint32 xWorkGroupCount = shaderExtent.width / workGroupSize.x() + (shaderExtent.width % workGroupSize.x() ? 1u : 0u);
279 				const deUint32 yWorkGroupCount = shaderExtent.height / workGroupSize.y() + (shaderExtent.height % workGroupSize.y() ? 1u : 0u);
280 				const deUint32 zWorkGroupCount = shaderExtent.depth / workGroupSize.z() + (shaderExtent.depth % workGroupSize.z() ? 1u : 0u);
281 
282 				const tcu::UVec3 maxComputeWorkGroupCount = tcu::UVec3(65535u, 65535u, 65535u);
283 
284 				if (maxComputeWorkGroupCount.x() < xWorkGroupCount ||
285 					maxComputeWorkGroupCount.y() < yWorkGroupCount ||
286 					maxComputeWorkGroupCount.z() < zWorkGroupCount)
287 				{
288 					TCU_THROW(NotSupportedError, "Image size is not supported");
289 				}
290 
291 				vkd.cmdDispatch(*commandBuffer, xWorkGroupCount, yWorkGroupCount, zWorkGroupCount);
292 			}
293 
294 			{
295 				const VkImageMemoryBarrier imageTransferBarrier = makeImageMemoryBarrier(VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *image, subresourceRange);
296 				vkd.cmdPipelineBarrier(*commandBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 0u, DE_NULL, 0u, DE_NULL, 1u, &imageTransferBarrier);
297 			}
298 		}
299 
300 		for (deUint32 planeNdx = 0; planeNdx < formatDescription.numPlanes; ++planeNdx)
301 		{
302 			planeOffsets[planeNdx]		= imageSizeInBytes;
303 			const deUint32	planeW		= imageCreateInfo.extent.width / (formatDescription.blockWidth * formatDescription.planes[planeNdx].widthDivisor);
304 			planeRowPitches[planeNdx]	= formatDescription.planes[planeNdx].elementSizeBytes * planeW;
305 			imageSizeInBytes			+= getPlaneSizeInBytes(formatDescription, makeExtent3D( params.size.x(), params.size.y(), params.size.z()) , planeNdx, 0u, BUFFER_IMAGE_COPY_OFFSET_GRANULARITY);
306 		}
307 
308 		const VkBufferCreateInfo		outputBufferCreateInfo	= makeBufferCreateInfo(imageSizeInBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT);
309 		const Unique<VkBuffer>			outputBuffer			( createBuffer(vkd, device, &outputBufferCreateInfo) );
310 		const de::UniquePtr<Allocation>	outputBufferAlloc		( bindBuffer(vkd, device, context.getDefaultAllocator(), *outputBuffer, MemoryRequirement::HostVisible) );
311 		std::vector<VkBufferImageCopy>	bufferImageCopy			( formatDescription.numPlanes );
312 
313 		for (deUint32 planeNdx = 0; planeNdx < formatDescription.numPlanes; ++planeNdx)
314 		{
315 			const VkImageAspectFlags	aspect = (formatDescription.numPlanes > 1) ? getPlaneAspect(planeNdx) : VK_IMAGE_ASPECT_COLOR_BIT;
316 
317 			bufferImageCopy[planeNdx] =
318 			{
319 				planeOffsets[planeNdx],																								//	VkDeviceSize				bufferOffset;
320 				0u,																													//	deUint32					bufferRowLength;
321 				0u,																													//	deUint32					bufferImageHeight;
322 				makeImageSubresourceLayers(aspect, 0u, 0u, 1u),																		//	VkImageSubresourceLayers	imageSubresource;
323 				makeOffset3D(0, 0, 0),																								//	VkOffset3D					imageOffset;
324 				getPlaneExtent(formatDescription, makeExtent3D(params.size.x(), params.size.y(), params.size.z()), planeNdx, 0u)	//	VkExtent3D					imageExtent;
325 			};
326 		}
327 		vkd.cmdCopyImageToBuffer(*commandBuffer, *image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *outputBuffer, static_cast<deUint32>(bufferImageCopy.size()), bufferImageCopy.data());
328 
329 		{
330 			const VkBufferMemoryBarrier outputBufferHostReadBarrier = makeBufferMemoryBarrier
331 			(
332 				VK_ACCESS_TRANSFER_WRITE_BIT,
333 				VK_ACCESS_HOST_READ_BIT,
334 				*outputBuffer,
335 				0u,
336 				imageSizeInBytes
337 			);
338 
339 			vkd.cmdPipelineBarrier(*commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 0u, DE_NULL, 1u, &outputBufferHostReadBarrier, 0u, DE_NULL);
340 		}
341 
342 		// End recording commands
343 		endCommandBuffer(vkd, *commandBuffer);
344 
345 		// Submit commands for execution and wait for completion
346 		submitCommandsAndWait(vkd, device, queue, *commandBuffer);
347 
348 		// Retrieve data from buffer to host memory
349 		invalidateAlloc(vkd, device, *outputBufferAlloc);
350 		deUint8*					outputData = static_cast<deUint8*>(outputBufferAlloc->getHostPtr());
351 
352 		for (deUint32 planeNdx = 0; planeNdx < formatDescription.numPlanes; ++planeNdx)
353 			planePointers[planeNdx] = outputData + static_cast<size_t>(planeOffsets[planeNdx]);
354 	}
355 
356 	// write result images to log file
357 	for (deUint32 channelNdx = 0; channelNdx < 4; ++channelNdx)
358 	{
359 		if (!formatDescription.hasChannelNdx(channelNdx))
360 			continue;
361 		deUint32					planeNdx					= formatDescription.channels[channelNdx].planeNdx;
362 		vk::VkFormat				planeCompatibleFormat		= getPlaneCompatibleFormatForWriting(formatDescription, planeNdx);
363 		vk::PlanarFormatDescription	compatibleFormatDescription	= (planeCompatibleFormat != getPlaneCompatibleFormat(formatDescription, planeNdx)) ? getPlanarFormatDescription(planeCompatibleFormat) : formatDescription;
364 		const tcu::UVec3			compatibleShaderGridSize	( params.size.x() / formatDescription.blockWidth, params.size.y() / formatDescription.blockHeight, params.size.z() / 1u );
365 		tcu::ConstPixelBufferAccess	pixelBuffer					= vk::getChannelAccess(compatibleFormatDescription, compatibleShaderGridSize, planeRowPitches, (const void* const*)planePointers, channelNdx);
366 		std::ostringstream str;
367 		str << "image" << channelNdx;
368 		context.getTestContext().getLog() << tcu::LogImage(str.str(), str.str(), pixelBuffer);
369 	}
370 
371 	// verify data
372 	const float					epsilon = 1e-5f;
373 	for (deUint32 channelNdx = 0; channelNdx < 4; ++channelNdx)
374 	{
375 		if (!formatDescription.hasChannelNdx(channelNdx))
376 			continue;
377 
378 		deUint32							planeNdx					= formatDescription.channels[channelNdx].planeNdx;
379 		vk::VkFormat						planeCompatibleFormat		= getPlaneCompatibleFormatForWriting(formatDescription, planeNdx);
380 		vk::PlanarFormatDescription			compatibleFormatDescription	= (planeCompatibleFormat != getPlaneCompatibleFormat(formatDescription, planeNdx)) ? getPlanarFormatDescription(planeCompatibleFormat) : formatDescription;
381 		const tcu::UVec3					compatibleShaderGridSize	( params.size.x() / formatDescription.blockWidth, params.size.y() / formatDescription.blockHeight, params.size.z() / 1u );
382 		VkExtent3D							compatibleImageSize			{ imageCreateInfo.extent.width / formatDescription.blockWidth, imageCreateInfo.extent.height / formatDescription.blockHeight, imageCreateInfo.extent.depth / 1u };
383 		tcu::ConstPixelBufferAccess			pixelBuffer					= vk::getChannelAccess(compatibleFormatDescription, compatibleShaderGridSize, planeRowPitches, (const void* const*)planePointers, channelNdx);
384 		VkExtent3D							planeExtent					= getPlaneExtent(compatibleFormatDescription, compatibleImageSize, planeNdx, 0u);
385 		tcu::IVec3							pixelDivider				= pixelBuffer.getDivider();
386 
387 		for (deUint32 offsetZ = 0u; offsetZ < planeExtent.depth; ++offsetZ)
388 		for (deUint32 offsetY = 0u; offsetY < planeExtent.height; ++offsetY)
389 		for (deUint32 offsetX = 0u; offsetX < planeExtent.width; ++offsetX)
390 		{
391 			deUint32	iReferenceValue;
392 			float		fReferenceValue;
393 			switch (channelNdx)
394 			{
395 				case 0:
396 					iReferenceValue = offsetX % 127u;
397 					fReferenceValue = static_cast<float>(iReferenceValue) / 127.f;
398 					break;
399 				case 1:
400 					iReferenceValue = offsetY % 127u;
401 					fReferenceValue = static_cast<float>(iReferenceValue) / 127.f;
402 					break;
403 				case 2:
404 					iReferenceValue = offsetZ % 127u;
405 					fReferenceValue = static_cast<float>(iReferenceValue) / 127.f;
406 					break;
407 				case 3:
408 					iReferenceValue = 0u;
409 					fReferenceValue = 0.f;
410 					break;
411 				default:	DE_FATAL("Unexpected channel index");	break;
412 			}
413 			float acceptableError = epsilon;
414 
415 			switch (formatDescription.channels[channelNdx].type)
416 			{
417 				case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER:
418 				case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER:
419 				{
420 					tcu::UVec4 outputValue = pixelBuffer.getPixelUint(offsetX * pixelDivider.x(), offsetY * pixelDivider.y(), 0);
421 
422 					if (outputValue.x() != iReferenceValue)
423 						return tcu::TestStatus::fail("Failed");
424 
425 					break;
426 				}
427 				case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT:
428 				case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT:
429 				{
430 					float fixedPointError = tcu::TexVerifierUtil::computeFixedPointError(formatDescription.channels[channelNdx].sizeBits);
431 					acceptableError += fixedPointError;
432 					tcu::Vec4 outputValue = pixelBuffer.getPixel(offsetX * pixelDivider.x(), offsetY * pixelDivider.y(), 0);
433 
434 					if (deAbs(outputValue.x() - fReferenceValue) > acceptableError)
435 						return tcu::TestStatus::fail("Failed");
436 
437 					break;
438 				}
439 				case tcu::TEXTURECHANNELCLASS_FLOATING_POINT:
440 				{
441 					const tcu::Vec4 outputValue = pixelBuffer.getPixel(offsetX * pixelDivider.x(), offsetY * pixelDivider.y(), 0);
442 
443 					if (deAbs( outputValue.x() - fReferenceValue) > acceptableError)
444 						return tcu::TestStatus::fail("Failed");
445 
446 					break;
447 				}
448 				default:	DE_FATAL("Unexpected channel type");	break;
449 			}
450 		}
451 	}
452 	return tcu::TestStatus::pass("Passed");
453 }
454 
getShaderImageType(const vk::PlanarFormatDescription & description)455 std::string getShaderImageType (const vk::PlanarFormatDescription& description)
456 {
457 	std::string	formatPart;
458 
459 	// all PlanarFormatDescription types have at least one channel ( 0 ) and all channel types are the same :
460 	switch (description.channels[0].type)
461 	{
462 		case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER:
463 			formatPart = "i";
464 			break;
465 		case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER:
466 			formatPart = "u";
467 			break;
468 		case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT:
469 		case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT:
470 		case tcu::TEXTURECHANNELCLASS_FLOATING_POINT:
471 			break;
472 
473 		default:
474 			DE_FATAL("Unexpected channel type");
475 	}
476 
477 	return formatPart + "image2D";
478 }
479 
getShaderImageDataType(const vk::PlanarFormatDescription & description)480 std::string getShaderImageDataType (const vk::PlanarFormatDescription& description)
481 {
482 	switch (description.channels[0].type)
483 	{
484 	case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER:
485 		return "uvec4";
486 	case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER:
487 		return "ivec4";
488 	case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT:
489 	case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT:
490 	case tcu::TEXTURECHANNELCLASS_FLOATING_POINT:
491 		return "vec4";
492 	default:
493 		DE_FATAL("Unexpected channel type");
494 		return "";
495 	}
496 }
497 
getFormatValueString(const std::vector<std::pair<deUint32,deUint32>> & channelsOnPlane,const std::vector<std::string> & formatValueStrings)498 std::string getFormatValueString	(const std::vector<std::pair<deUint32, deUint32>>& channelsOnPlane,
499 									 const std::vector<std::string>& formatValueStrings)
500 {
501 	std::string result = "( ";
502 	deUint32 i;
503 	for (i=0; i<channelsOnPlane.size(); ++i)
504 	{
505 		result += formatValueStrings[channelsOnPlane[i].first];
506 		if (i < 3)
507 			result += ", ";
508 	}
509 	for (; i < 4; ++i)
510 	{
511 		result += "0";
512 		if (i < 3)
513 			result += ", ";
514 	}
515 	result += " )";
516 	return result;
517 }
518 
getShaderImageFormatQualifier(VkFormat format)519 std::string getShaderImageFormatQualifier (VkFormat format)
520 {
521 	switch (format)
522 	{
523 		case VK_FORMAT_R8_SINT:										return "r8i";
524 		case VK_FORMAT_R16_SINT:									return "r16i";
525 		case VK_FORMAT_R32_SINT:									return "r32i";
526 		case VK_FORMAT_R8_UINT:										return "r8ui";
527 		case VK_FORMAT_R16_UINT:									return "r16ui";
528 		case VK_FORMAT_R32_UINT:									return "r32ui";
529 		case VK_FORMAT_R8_SNORM:									return "r8_snorm";
530 		case VK_FORMAT_R16_SNORM:									return "r16_snorm";
531 		case VK_FORMAT_R8_UNORM:									return "r8";
532 		case VK_FORMAT_R16_UNORM:									return "r16";
533 
534 		case VK_FORMAT_R8G8_SINT:									return "rg8i";
535 		case VK_FORMAT_R16G16_SINT:									return "rg16i";
536 		case VK_FORMAT_R32G32_SINT:									return "rg32i";
537 		case VK_FORMAT_R8G8_UINT:									return "rg8ui";
538 		case VK_FORMAT_R16G16_UINT:									return "rg16ui";
539 		case VK_FORMAT_R32G32_UINT:									return "rg32ui";
540 		case VK_FORMAT_R8G8_SNORM:									return "rg8_snorm";
541 		case VK_FORMAT_R16G16_SNORM:								return "rg16_snorm";
542 		case VK_FORMAT_R8G8_UNORM:									return "rg8";
543 		case VK_FORMAT_R16G16_UNORM:								return "rg16";
544 
545 		case VK_FORMAT_R8G8B8A8_SINT:								return "rgba8i";
546 		case VK_FORMAT_R16G16B16A16_SINT:							return "rgba16i";
547 		case VK_FORMAT_R32G32B32A32_SINT:							return "rgba32i";
548 		case VK_FORMAT_R8G8B8A8_UINT:								return "rgba8ui";
549 		case VK_FORMAT_R16G16B16A16_UINT:							return "rgba16ui";
550 		case VK_FORMAT_R32G32B32A32_UINT:							return "rgba32ui";
551 		case VK_FORMAT_R8G8B8A8_SNORM:								return "rgba8_snorm";
552 		case VK_FORMAT_R16G16B16A16_SNORM:							return "rgba16_snorm";
553 		case VK_FORMAT_R8G8B8A8_UNORM:								return "rgba8";
554 		case VK_FORMAT_R16G16B16A16_UNORM:							return "rgba16";
555 
556 		case VK_FORMAT_G8B8G8R8_422_UNORM:							return "rgba8";
557 		case VK_FORMAT_B8G8R8G8_422_UNORM:							return "rgba8";
558 		case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM:					return "rgba8";
559 		case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM:					return "rgba8";
560 		case VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM:					return "rgba8";
561 		case VK_FORMAT_G8_B8R8_2PLANE_422_UNORM:					return "rgba8";
562 		case VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM:					return "rgba8";
563 		case VK_FORMAT_R10X6_UNORM_PACK16:							return "r16";
564 		case VK_FORMAT_R10X6G10X6_UNORM_2PACK16:					return "rg16";
565 		case VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16:			return "rgba16";
566 		case VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16:		return "rgba16";
567 		case VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16:		return "rgba16";
568 		case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16:	return "rgba16";
569 		case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16:	return "rgba16";
570 		case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16:	return "rgba16";
571 		case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16:	return "rgba16";
572 		case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16:	return "rgba16";
573 		case VK_FORMAT_R12X4_UNORM_PACK16:							return "r16";
574 		case VK_FORMAT_R12X4G12X4_UNORM_2PACK16:					return "rg16";
575 		case VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16:			return "rgba16";
576 		case VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16:		return "rgba16";
577 		case VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16:		return "rgba16";
578 		case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16:	return "rgba16";
579 		case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16:	return "rgba16";
580 		case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16:	return "rgba16";
581 		case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16:	return "rgba16";
582 		case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16:	return "rgba16";
583 		case VK_FORMAT_G16B16G16R16_422_UNORM:						return "rgba16";
584 		case VK_FORMAT_B16G16R16G16_422_UNORM:						return "rgba16";
585 		case VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM:				return "rgba16";
586 		case VK_FORMAT_G16_B16R16_2PLANE_420_UNORM:					return "rgba16";
587 		case VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM:				return "rgba16";
588 		case VK_FORMAT_G16_B16R16_2PLANE_422_UNORM:					return "rgba16";
589 		case VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM:				return "rgba16";
590 		case VK_FORMAT_G8_B8R8_2PLANE_444_UNORM_EXT:				return "rgba8";
591 		case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16_EXT:return "rgba16";
592 		case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16_EXT:return "rgba16";
593 		case VK_FORMAT_G16_B16R16_2PLANE_444_UNORM_EXT:				return "rgba16";
594 
595 		default:
596 			DE_FATAL("Unexpected texture format");
597 			return "error";
598 	}
599 }
600 
initPrograms(SourceCollections & sourceCollections,TestParameters params)601 void initPrograms (SourceCollections& sourceCollections, TestParameters params)
602 {
603 	// Create compute program
604 	const char* const				versionDecl			= glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_440);
605 	const PlanarFormatDescription	formatDescription	= getPlanarFormatDescription(params.format);
606 	const std::string				imageTypeStr		= getShaderImageType(formatDescription);
607 	const std::string				formatDataStr		= getShaderImageDataType(formatDescription);
608 	const tcu::UVec3				shaderGridSize		( params.size.x(), params.size.y(), params.size.z() );
609 
610 	std::vector<std::string>		formatValueStrings;
611 	switch (formatDescription.channels[0].type)
612 	{
613 	case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER:
614 	case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER:
615 		formatValueStrings = {
616 			"int(gl_GlobalInvocationID.x) % 127",
617 			"int(gl_GlobalInvocationID.y) % 127",
618 			"int(gl_GlobalInvocationID.z) % 127",
619 			"1"
620 		};
621 		break;
622 	case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT:
623 	case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT:
624 	case tcu::TEXTURECHANNELCLASS_FLOATING_POINT:
625 		formatValueStrings = {
626 			"float(int(gl_GlobalInvocationID.x) % 127) / 127.0" ,
627 			"float(int(gl_GlobalInvocationID.y) % 127) / 127.0",
628 			"float(int(gl_GlobalInvocationID.z) % 127) / 127.0",
629 			"1.0"
630 		};
631 		break;
632 	default:	DE_ASSERT(false);	break;
633 	}
634 
635 	for (deUint32 planeNdx = 0; planeNdx < formatDescription.numPlanes; ++planeNdx)
636 	{
637 		VkFormat						planeCompatibleFormat		= getPlaneCompatibleFormatForWriting(formatDescription, planeNdx);
638 		vk::PlanarFormatDescription		compatibleFormatDescription	= (planeCompatibleFormat != getPlaneCompatibleFormat(formatDescription, planeNdx)) ? getPlanarFormatDescription(planeCompatibleFormat) : formatDescription;
639 		VkExtent3D						compatibleShaderGridSize	{ shaderGridSize.x() / formatDescription.blockWidth, shaderGridSize.y() / formatDescription.blockHeight, shaderGridSize.z() / 1u };
640 
641 		std::vector<std::pair<deUint32, deUint32>> channelsOnPlane;
642 		for (deUint32 channelNdx = 0; channelNdx < 4; ++channelNdx)
643 		{
644 			if (!formatDescription.hasChannelNdx(channelNdx))
645 				continue;
646 			if (formatDescription.channels[channelNdx].planeNdx != planeNdx)
647 				continue;
648 			channelsOnPlane.push_back({ channelNdx,formatDescription.channels[channelNdx].offsetBits });
649 		}
650 		// reorder channels for multi-planar images
651 		if (formatDescription.numPlanes > 1)
652 			std::sort(begin(channelsOnPlane), end(channelsOnPlane), [](const std::pair<deUint32, deUint32>& lhs, const std::pair<deUint32, deUint32>& rhs) { return lhs.second < rhs.second; });
653 		std::string			formatValueStr		= getFormatValueString(channelsOnPlane, formatValueStrings);
654 		VkExtent3D			shaderExtent		= getPlaneExtent(compatibleFormatDescription, compatibleShaderGridSize, planeNdx, 0);
655 		const std::string	formatQualifierStr	= getShaderImageFormatQualifier(formatDescription.planes[planeNdx].planeCompatibleFormat);
656 		const tcu::UVec3	workGroupSize		= computeWorkGroupSize(shaderExtent);
657 
658 		std::ostringstream src;
659 		src << versionDecl << "\n"
660 			<< "layout (local_size_x = " << workGroupSize.x() << ", local_size_y = " << workGroupSize.y() << ", local_size_z = " << workGroupSize.z() << ") in; \n"
661 			<< "layout (binding = 0, " << formatQualifierStr << ") writeonly uniform highp " << imageTypeStr << " u_image;\n"
662 			<< "void main (void)\n"
663 			<< "{\n"
664 			<< "	if( gl_GlobalInvocationID.x < " << shaderExtent.width << " ) \n"
665 			<< "	if( gl_GlobalInvocationID.y < " << shaderExtent.height << " ) \n"
666 			<< "	if( gl_GlobalInvocationID.z < " << shaderExtent.depth << " ) \n"
667 			<< "	{\n"
668 			<< "		imageStore(u_image, ivec2( gl_GlobalInvocationID.x, gl_GlobalInvocationID.y ) ,"
669 			<< formatDataStr << formatValueStr << ");\n"
670 			<< "	}\n"
671 			<< "}\n";
672 		std::ostringstream shaderName;
673 		shaderName << "comp" << planeNdx;
674 		sourceCollections.glslSources.add(shaderName.str()) << glu::ComputeSource(src.str());
675 	}
676 }
677 
populateStorageImageWriteFormatGroup(tcu::TestContext & testCtx,de::MovePtr<tcu::TestCaseGroup> testGroup)678 tcu::TestCaseGroup* populateStorageImageWriteFormatGroup (tcu::TestContext& testCtx, de::MovePtr<tcu::TestCaseGroup> testGroup)
679 {
680 	const std::vector<tcu::UVec3>	availableSizes{ tcu::UVec3(512u, 512u, 1u), tcu::UVec3(1024u, 128u, 1u), tcu::UVec3(66u, 32u, 1u) };
681 
682 	auto addTests = [&](int formatNdx)
683 	{
684 		const VkFormat					format				= (VkFormat)formatNdx;
685 		tcu::UVec3						imageSizeAlignment	= getImageSizeAlignment(format);
686 		std::string						formatName			= de::toLower(de::toString(format).substr(10));
687 		de::MovePtr<tcu::TestCaseGroup> formatGroup			( new tcu::TestCaseGroup(testCtx, formatName.c_str(), "") );
688 
689 		for (size_t sizeNdx = 0; sizeNdx < availableSizes.size(); sizeNdx++)
690 		{
691 			const tcu::UVec3 imageSize = availableSizes[sizeNdx];
692 
693 			// skip test for images with odd sizes for some YCbCr formats
694 			if ((imageSize.x() % imageSizeAlignment.x()) != 0)
695 				continue;
696 			if ((imageSize.y() % imageSizeAlignment.y()) != 0)
697 				continue;
698 
699 			std::ostringstream stream;
700 			stream << imageSize.x() << "_" << imageSize.y() << "_" << imageSize.z();
701 			de::MovePtr<tcu::TestCaseGroup> sizeGroup(new tcu::TestCaseGroup(testCtx, stream.str().c_str(), ""));
702 
703 			addFunctionCaseWithPrograms(sizeGroup.get(), "joint", "", checkSupport, initPrograms, testStorageImageWrite, TestParameters(format, imageSize, 0u));
704 			addFunctionCaseWithPrograms(sizeGroup.get(), "disjoint", "", checkSupport, initPrograms, testStorageImageWrite, TestParameters(format, imageSize, (VkImageCreateFlags)VK_IMAGE_CREATE_DISJOINT_BIT));
705 
706 			formatGroup->addChild(sizeGroup.release());
707 		}
708 		testGroup->addChild(formatGroup.release());
709 	};
710 
711 	for (int formatNdx = VK_YCBCR_FORMAT_FIRST; formatNdx < VK_YCBCR_FORMAT_LAST; formatNdx++)
712 	{
713 		addTests(formatNdx);
714 	}
715 
716 	for (int formatNdx = VK_FORMAT_G8_B8R8_2PLANE_444_UNORM_EXT; formatNdx <= VK_FORMAT_G16_B16R16_2PLANE_444_UNORM_EXT; formatNdx++)
717 	{
718 		addTests(formatNdx);
719 	}
720 
721 	return testGroup.release();
722 }
723 
724 } // namespace
725 
createStorageImageWriteTests(tcu::TestContext & testCtx)726 tcu::TestCaseGroup* createStorageImageWriteTests (tcu::TestContext& testCtx)
727 {
728 	de::MovePtr<tcu::TestCaseGroup> testGroup(new tcu::TestCaseGroup(testCtx, "storage_image_write", "Writing to YCbCr storage images"));
729 	return populateStorageImageWriteFormatGroup(testCtx, testGroup);
730 }
731 
732 } // ycbcr
733 } // vkt
734