vktSparseResourcesImageSparseBinding.cpp revision a6fbec3926163fbfc0ad45b21f7b7e2f8aae7adf
1/*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2016 The Khronos Group 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  vktSparseResourcesImageSparseBinding.cpp
21 * \brief Sparse fully resident images with mipmaps tests
22 *//*--------------------------------------------------------------------*/
23
24#include "vktSparseResourcesBufferSparseBinding.hpp"
25#include "vktSparseResourcesTestsUtil.hpp"
26#include "vktSparseResourcesBase.hpp"
27#include "vktTestCaseUtil.hpp"
28
29#include "vkDefs.hpp"
30#include "vkRef.hpp"
31#include "vkRefUtil.hpp"
32#include "vkPlatform.hpp"
33#include "vkPrograms.hpp"
34#include "vkMemUtil.hpp"
35#include "vkBuilderUtil.hpp"
36#include "vkImageUtil.hpp"
37#include "vkQueryUtil.hpp"
38#include "vkTypeUtil.hpp"
39
40#include "deUniquePtr.hpp"
41#include "deStringUtil.hpp"
42
43#include <string>
44#include <vector>
45
46using namespace vk;
47
48namespace vkt
49{
50namespace sparse
51{
52namespace
53{
54
55class ImageSparseBindingCase : public TestCase
56{
57public:
58					ImageSparseBindingCase	(tcu::TestContext&			testCtx,
59											 const std::string&			name,
60											 const std::string&			description,
61											 const ImageType			imageType,
62											 const tcu::UVec3&			imageSize,
63											 const tcu::TextureFormat&	format);
64
65	TestInstance*	createInstance			(Context&					context) const;
66
67private:
68	const ImageType				m_imageType;
69	const tcu::UVec3			m_imageSize;
70	const tcu::TextureFormat	m_format;
71};
72
73ImageSparseBindingCase::ImageSparseBindingCase (tcu::TestContext&			testCtx,
74												const std::string&			name,
75												const std::string&			description,
76												const ImageType				imageType,
77												const tcu::UVec3&			imageSize,
78												const tcu::TextureFormat&	format)
79	: TestCase				(testCtx, name, description)
80	, m_imageType			(imageType)
81	, m_imageSize			(imageSize)
82	, m_format				(format)
83{
84}
85
86class ImageSparseBindingInstance : public SparseResourcesBaseInstance
87{
88public:
89					ImageSparseBindingInstance	(Context&					context,
90												 const ImageType			imageType,
91												 const tcu::UVec3&			imageSize,
92												 const tcu::TextureFormat&	format);
93
94	tcu::TestStatus	iterate						(void);
95
96private:
97	const ImageType				m_imageType;
98	const tcu::UVec3			m_imageSize;
99	const tcu::TextureFormat	m_format;
100};
101
102ImageSparseBindingInstance::ImageSparseBindingInstance (Context&					context,
103														const ImageType				imageType,
104														const tcu::UVec3&			imageSize,
105														const tcu::TextureFormat&	format)
106	: SparseResourcesBaseInstance	(context)
107	, m_imageType					(imageType)
108	, m_imageSize					(imageSize)
109	, m_format						(format)
110{
111}
112
113tcu::TestStatus ImageSparseBindingInstance::iterate (void)
114{
115	const InstanceInterface&	instance		= m_context.getInstanceInterface();
116	const DeviceInterface&		deviceInterface = m_context.getDeviceInterface();
117	const VkPhysicalDevice		physicalDevice	= m_context.getPhysicalDevice();
118	VkImageCreateInfo			imageSparseInfo;
119	std::vector<DeviceMemorySp>	deviceMemUniquePtrVec;
120
121	// Check if image size does not exceed device limits
122	if (!isImageSizeSupported(instance, physicalDevice, m_imageType, m_imageSize))
123		TCU_THROW(NotSupportedError, "Image size not supported for device");
124
125	// Check if device supports sparse binding
126	if (!getPhysicalDeviceFeatures(instance, physicalDevice).sparseBinding)
127		TCU_THROW(NotSupportedError, "Device does not support sparse binding");
128
129	{
130		// Create logical device supporting both sparse and compute queues
131		QueueRequirementsVec queueRequirements;
132		queueRequirements.push_back(QueueRequirements(VK_QUEUE_SPARSE_BINDING_BIT, 1u));
133		queueRequirements.push_back(QueueRequirements(VK_QUEUE_COMPUTE_BIT, 1u));
134
135		createDeviceSupportingQueues(queueRequirements);
136	}
137
138	const Queue& sparseQueue	= getQueue(VK_QUEUE_SPARSE_BINDING_BIT, 0);
139	const Queue& computeQueue	= getQueue(VK_QUEUE_COMPUTE_BIT, 0);
140
141	imageSparseInfo.sType					= VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;					//VkStructureType		sType;
142	imageSparseInfo.pNext					= DE_NULL;												//const void*			pNext;
143	imageSparseInfo.flags					= VK_IMAGE_CREATE_SPARSE_BINDING_BIT;					//VkImageCreateFlags	flags;
144	imageSparseInfo.imageType				= mapImageType(m_imageType);							//VkImageType			imageType;
145	imageSparseInfo.format					= mapTextureFormat(m_format);							//VkFormat				format;
146	imageSparseInfo.extent					= makeExtent3D(getLayerSize(m_imageType, m_imageSize));	//VkExtent3D			extent;
147	imageSparseInfo.arrayLayers				= getNumLayers(m_imageType, m_imageSize);				//deUint32				arrayLayers;
148	imageSparseInfo.samples					= VK_SAMPLE_COUNT_1_BIT;								//VkSampleCountFlagBits	samples;
149	imageSparseInfo.tiling					= VK_IMAGE_TILING_OPTIMAL;								//VkImageTiling			tiling;
150	imageSparseInfo.initialLayout			= VK_IMAGE_LAYOUT_UNDEFINED;							//VkImageLayout			initialLayout;
151	imageSparseInfo.usage					= VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
152											  VK_IMAGE_USAGE_TRANSFER_DST_BIT;						//VkImageUsageFlags		usage;
153	imageSparseInfo.sharingMode				= VK_SHARING_MODE_EXCLUSIVE;							//VkSharingMode			sharingMode;
154	imageSparseInfo.queueFamilyIndexCount	= 0u;													//deUint32				queueFamilyIndexCount;
155	imageSparseInfo.pQueueFamilyIndices		= DE_NULL;												//const deUint32*		pQueueFamilyIndices;
156
157	if (m_imageType == IMAGE_TYPE_CUBE || m_imageType == IMAGE_TYPE_CUBE_ARRAY)
158	{
159		imageSparseInfo.flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
160	}
161
162	{
163		VkImageFormatProperties imageFormatProperties;
164		instance.getPhysicalDeviceImageFormatProperties(physicalDevice,
165			imageSparseInfo.format,
166			imageSparseInfo.imageType,
167			imageSparseInfo.tiling,
168			imageSparseInfo.usage,
169			imageSparseInfo.flags,
170			&imageFormatProperties);
171
172		imageSparseInfo.mipLevels = getImageMaxMipLevels(imageFormatProperties, imageSparseInfo.extent);
173	}
174
175	// Create sparse image
176	const Unique<VkImage> imageSparse(createImage(deviceInterface, getDevice(), &imageSparseInfo));
177
178	// Create sparse image memory bind semaphore
179	const Unique<VkSemaphore> imageMemoryBindSemaphore(makeSemaphore(deviceInterface, getDevice()));
180
181	// Get sparse image general memory requirements
182	const VkMemoryRequirements imageSparseMemRequirements = getImageMemoryRequirements(deviceInterface, getDevice(), *imageSparse);
183
184	// Check if required image memory size does not exceed device limits
185	if (imageSparseMemRequirements.size > getPhysicalDeviceProperties(instance, physicalDevice).limits.sparseAddressSpaceSize)
186		TCU_THROW(NotSupportedError, "Required memory size for sparse resource exceeds device limits");
187
188	DE_ASSERT((imageSparseMemRequirements.size % imageSparseMemRequirements.alignment) == 0);
189
190	{
191		std::vector<VkSparseMemoryBind>	sparseMemoryBinds;
192		const deUint32					numSparseBinds	= static_cast<deUint32>(imageSparseMemRequirements.size / imageSparseMemRequirements.alignment);
193		const deUint32					memoryType		= findMatchingMemoryType(instance, physicalDevice, imageSparseMemRequirements, MemoryRequirement::Any);
194
195		if (memoryType == NO_MATCH_FOUND)
196			return tcu::TestStatus::fail("No matching memory type found");
197
198		for (deUint32 sparseBindNdx = 0; sparseBindNdx < numSparseBinds; ++sparseBindNdx)
199		{
200			const VkSparseMemoryBind sparseMemoryBind = makeSparseMemoryBind(deviceInterface, getDevice(),
201				imageSparseMemRequirements.alignment, memoryType, imageSparseMemRequirements.alignment * sparseBindNdx);
202
203			deviceMemUniquePtrVec.push_back(makeVkSharedPtr(Move<VkDeviceMemory>(check<VkDeviceMemory>(sparseMemoryBind.memory), Deleter<VkDeviceMemory>(deviceInterface, getDevice(), DE_NULL))));
204
205			sparseMemoryBinds.push_back(sparseMemoryBind);
206		}
207
208		const VkSparseImageOpaqueMemoryBindInfo opaqueBindInfo = makeSparseImageOpaqueMemoryBindInfo(*imageSparse, numSparseBinds, &sparseMemoryBinds[0]);
209
210		const VkBindSparseInfo bindSparseInfo =
211		{
212			VK_STRUCTURE_TYPE_BIND_SPARSE_INFO,			//VkStructureType							sType;
213			DE_NULL,									//const void*								pNext;
214			0u,											//deUint32									waitSemaphoreCount;
215			DE_NULL,									//const VkSemaphore*						pWaitSemaphores;
216			0u,											//deUint32									bufferBindCount;
217			DE_NULL,									//const VkSparseBufferMemoryBindInfo*		pBufferBinds;
218			1u,											//deUint32									imageOpaqueBindCount;
219			&opaqueBindInfo,							//const VkSparseImageOpaqueMemoryBindInfo*	pImageOpaqueBinds;
220			0u,											//deUint32									imageBindCount;
221			DE_NULL,									//const VkSparseImageMemoryBindInfo*		pImageBinds;
222			1u,											//deUint32									signalSemaphoreCount;
223			&imageMemoryBindSemaphore.get()				//const VkSemaphore*						pSignalSemaphores;
224		};
225
226		// Submit sparse bind commands for execution
227		VK_CHECK(deviceInterface.queueBindSparse(sparseQueue.queueHandle, 1u, &bindSparseInfo, DE_NULL));
228	}
229
230	// Create command buffer for compute and transfer oparations
231	const Unique<VkCommandPool>	  commandPool(makeCommandPool(deviceInterface, getDevice(), computeQueue.queueFamilyIndex));
232	const Unique<VkCommandBuffer> commandBuffer(makeCommandBuffer(deviceInterface, getDevice(), *commandPool));
233
234	std::vector<VkBufferImageCopy> bufferImageCopy(imageSparseInfo.mipLevels);
235
236	{
237		deUint32 bufferOffset = 0;
238		for (deUint32 mipmapNdx = 0; mipmapNdx < imageSparseInfo.mipLevels; mipmapNdx++)
239		{
240			bufferImageCopy[mipmapNdx] = makeBufferImageCopy(mipLevelExtents(imageSparseInfo.extent, mipmapNdx), imageSparseInfo.arrayLayers, mipmapNdx, static_cast<VkDeviceSize>(bufferOffset));
241			bufferOffset += getImageMipLevelSizeInBytes(imageSparseInfo.extent, imageSparseInfo.arrayLayers, m_format, mipmapNdx, BUFFER_IMAGE_COPY_OFFSET_GRANULARITY);
242		}
243	}
244
245	// Start recording commands
246	beginCommandBuffer(deviceInterface, *commandBuffer);
247
248	const deUint32					imageSizeInBytes		= getImageSizeInBytes(imageSparseInfo.extent, imageSparseInfo.arrayLayers, m_format, imageSparseInfo.mipLevels, BUFFER_IMAGE_COPY_OFFSET_GRANULARITY);
249	const VkBufferCreateInfo		inputBufferCreateInfo	= makeBufferCreateInfo(imageSizeInBytes, VK_BUFFER_USAGE_TRANSFER_SRC_BIT);
250	const Unique<VkBuffer>			inputBuffer				(createBuffer(deviceInterface, getDevice(), &inputBufferCreateInfo));
251	const de::UniquePtr<Allocation>	inputBufferAlloc		(bindBuffer(deviceInterface, getDevice(), getAllocator(), *inputBuffer, MemoryRequirement::HostVisible));
252
253	std::vector<deUint8> referenceData(imageSizeInBytes);
254
255	for (deUint32 valueNdx = 0; valueNdx < imageSizeInBytes; ++valueNdx)
256	{
257		referenceData[valueNdx] = static_cast<deUint8>((valueNdx % imageSparseMemRequirements.alignment) + 1u);
258	}
259
260	deMemcpy(inputBufferAlloc->getHostPtr(), &referenceData[0], imageSizeInBytes);
261
262	flushMappedMemoryRange(deviceInterface, getDevice(), inputBufferAlloc->getMemory(), inputBufferAlloc->getOffset(), imageSizeInBytes);
263
264	{
265		const VkBufferMemoryBarrier inputBufferBarrier = makeBufferMemoryBarrier
266		(
267			VK_ACCESS_HOST_WRITE_BIT,
268			VK_ACCESS_TRANSFER_READ_BIT,
269			*inputBuffer,
270			0u,
271			imageSizeInBytes
272		);
273
274		deviceInterface.cmdPipelineBarrier(*commandBuffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 0u, DE_NULL, 1u, &inputBufferBarrier, 0u, DE_NULL);
275	}
276
277	{
278		const VkImageMemoryBarrier imageSparseTransferDstBarrier = makeImageMemoryBarrier
279		(
280			0u,
281			VK_ACCESS_TRANSFER_WRITE_BIT,
282			VK_IMAGE_LAYOUT_UNDEFINED,
283			VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
284			sparseQueue.queueFamilyIndex != computeQueue.queueFamilyIndex ? sparseQueue.queueFamilyIndex : VK_QUEUE_FAMILY_IGNORED,
285			sparseQueue.queueFamilyIndex != computeQueue.queueFamilyIndex ? computeQueue.queueFamilyIndex : VK_QUEUE_FAMILY_IGNORED,
286			*imageSparse,
287			makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, imageSparseInfo.mipLevels, 0u, imageSparseInfo.arrayLayers)
288		);
289
290		deviceInterface.cmdPipelineBarrier(*commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 0u, DE_NULL, 0u, DE_NULL, 1u, &imageSparseTransferDstBarrier);
291	}
292
293	deviceInterface.cmdCopyBufferToImage(*commandBuffer, *inputBuffer, *imageSparse, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, static_cast<deUint32>(bufferImageCopy.size()), &bufferImageCopy[0]);
294
295	{
296		const VkImageMemoryBarrier imageSparseTransferSrcBarrier = makeImageMemoryBarrier
297		(
298			VK_ACCESS_TRANSFER_WRITE_BIT,
299			VK_ACCESS_TRANSFER_READ_BIT,
300			VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
301			VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
302			*imageSparse,
303			makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, imageSparseInfo.mipLevels, 0u, imageSparseInfo.arrayLayers)
304		);
305
306		deviceInterface.cmdPipelineBarrier(*commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 0u, DE_NULL, 0u, DE_NULL, 1u, &imageSparseTransferSrcBarrier);
307	}
308
309	const VkBufferCreateInfo		outputBufferCreateInfo	= makeBufferCreateInfo(imageSizeInBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT);
310	const Unique<VkBuffer>			outputBuffer			(createBuffer(deviceInterface, getDevice(), &outputBufferCreateInfo));
311	const de::UniquePtr<Allocation>	outputBufferAlloc		(bindBuffer(deviceInterface, getDevice(), getAllocator(), *outputBuffer, MemoryRequirement::HostVisible));
312
313	deviceInterface.cmdCopyImageToBuffer(*commandBuffer, *imageSparse, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *outputBuffer, static_cast<deUint32>(bufferImageCopy.size()), &bufferImageCopy[0]);
314
315	{
316		const VkBufferMemoryBarrier outputBufferBarrier = makeBufferMemoryBarrier
317		(
318			VK_ACCESS_TRANSFER_WRITE_BIT,
319			VK_ACCESS_HOST_READ_BIT,
320			*outputBuffer,
321			0u,
322			imageSizeInBytes
323		);
324
325		deviceInterface.cmdPipelineBarrier(*commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 0u, DE_NULL, 1u, &outputBufferBarrier, 0u, DE_NULL);
326	}
327
328	// End recording commands
329	endCommandBuffer(deviceInterface, *commandBuffer);
330
331	const VkPipelineStageFlags stageBits[] = { VK_PIPELINE_STAGE_TRANSFER_BIT };
332
333	// Submit commands for execution and wait for completion
334	submitCommandsAndWait(deviceInterface, getDevice(), computeQueue.queueHandle, *commandBuffer, 1u, &imageMemoryBindSemaphore.get(), stageBits);
335
336	// Retrieve data from buffer to host memory
337	invalidateMappedMemoryRange(deviceInterface, getDevice(), outputBufferAlloc->getMemory(), outputBufferAlloc->getOffset(), imageSizeInBytes);
338
339	const deUint8* outputData = static_cast<const deUint8*>(outputBufferAlloc->getHostPtr());
340
341	// Wait for sparse queue to become idle
342	deviceInterface.queueWaitIdle(sparseQueue.queueHandle);
343
344	for (deUint32 mipmapNdx = 0; mipmapNdx < imageSparseInfo.mipLevels; ++mipmapNdx)
345	{
346		const deUint32 mipLevelSizeInBytes	= getImageMipLevelSizeInBytes(imageSparseInfo.extent, imageSparseInfo.arrayLayers, m_format, mipmapNdx);
347		const deUint32 bufferOffset			= static_cast<deUint32>(bufferImageCopy[mipmapNdx].bufferOffset);
348
349		if (deMemCmp(outputData + bufferOffset, &referenceData[bufferOffset], mipLevelSizeInBytes) != 0)
350			return tcu::TestStatus::fail("Failed");
351	}
352
353	return tcu::TestStatus::pass("Passed");
354}
355
356TestInstance* ImageSparseBindingCase::createInstance (Context& context) const
357{
358	return new ImageSparseBindingInstance(context, m_imageType, m_imageSize, m_format);
359}
360
361} // anonymous ns
362
363tcu::TestCaseGroup* createImageSparseBindingTests(tcu::TestContext& testCtx)
364{
365	de::MovePtr<tcu::TestCaseGroup> testGroup(new tcu::TestCaseGroup(testCtx, "image_sparse_binding", "Buffer Sparse Binding"));
366
367	static const deUint32 sizeCountPerImageType = 3u;
368
369	struct ImageParameters
370	{
371		ImageType	imageType;
372		tcu::UVec3	imageSizes[sizeCountPerImageType];
373	};
374
375	static const ImageParameters imageParametersArray[] =
376	{
377		{ IMAGE_TYPE_1D,		{ tcu::UVec3(512u, 1u,   1u ), tcu::UVec3(1024u, 1u,   1u), tcu::UVec3(11u,  1u,   1u) } },
378		{ IMAGE_TYPE_1D_ARRAY,  { tcu::UVec3(512u, 1u,   64u), tcu::UVec3(1024u, 1u,   8u), tcu::UVec3(11u,  1u,   3u) } },
379		{ IMAGE_TYPE_2D,		{ tcu::UVec3(512u, 256u, 1u ), tcu::UVec3(1024u, 128u, 1u), tcu::UVec3(11u,  137u, 1u) } },
380		{ IMAGE_TYPE_2D_ARRAY,	{ tcu::UVec3(512u, 256u, 6u ), tcu::UVec3(1024u, 128u, 8u), tcu::UVec3(11u,  137u, 3u) } },
381		{ IMAGE_TYPE_3D,		{ tcu::UVec3(512u, 256u, 6u ), tcu::UVec3(1024u, 128u, 8u), tcu::UVec3(11u,  137u, 3u) } },
382		{ IMAGE_TYPE_CUBE,		{ tcu::UVec3(256u, 256u, 1u ), tcu::UVec3(128u,  128u, 1u), tcu::UVec3(137u, 137u, 1u) } },
383		{ IMAGE_TYPE_CUBE_ARRAY,{ tcu::UVec3(256u, 256u, 6u ), tcu::UVec3(128u,  128u, 8u), tcu::UVec3(137u, 137u, 3u) } }
384	};
385
386	static const tcu::TextureFormat formats[] =
387	{
388		tcu::TextureFormat(tcu::TextureFormat::R,		tcu::TextureFormat::SIGNED_INT32),
389		tcu::TextureFormat(tcu::TextureFormat::R,		tcu::TextureFormat::SIGNED_INT16),
390		tcu::TextureFormat(tcu::TextureFormat::R,		tcu::TextureFormat::SIGNED_INT8),
391		tcu::TextureFormat(tcu::TextureFormat::RGBA,	tcu::TextureFormat::UNSIGNED_INT32),
392		tcu::TextureFormat(tcu::TextureFormat::RGBA,	tcu::TextureFormat::UNSIGNED_INT16),
393		tcu::TextureFormat(tcu::TextureFormat::RGBA,	tcu::TextureFormat::UNSIGNED_INT8)
394	};
395
396	for (deInt32 imageTypeNdx = 0; imageTypeNdx < DE_LENGTH_OF_ARRAY(imageParametersArray); ++imageTypeNdx)
397	{
398		const ImageType					imageType = imageParametersArray[imageTypeNdx].imageType;
399		de::MovePtr<tcu::TestCaseGroup> imageTypeGroup(new tcu::TestCaseGroup(testCtx, getImageTypeName(imageType).c_str(), ""));
400
401		for (deInt32 formatNdx = 0; formatNdx < DE_LENGTH_OF_ARRAY(formats); ++formatNdx)
402		{
403			const tcu::TextureFormat&		format = formats[formatNdx];
404			de::MovePtr<tcu::TestCaseGroup> formatGroup(new tcu::TestCaseGroup(testCtx, getShaderImageFormatQualifier(format).c_str(), ""));
405
406			for (deInt32 imageSizeNdx = 0; imageSizeNdx < DE_LENGTH_OF_ARRAY(imageParametersArray[imageTypeNdx].imageSizes); ++imageSizeNdx)
407			{
408				const tcu::UVec3 imageSize = imageParametersArray[imageTypeNdx].imageSizes[imageSizeNdx];
409
410				std::ostringstream stream;
411				stream << imageSize.x() << "_" << imageSize.y() << "_" << imageSize.z();
412
413				formatGroup->addChild(new ImageSparseBindingCase(testCtx, stream.str(), "", imageType, imageSize, format));
414			}
415			imageTypeGroup->addChild(formatGroup.release());
416		}
417		testGroup->addChild(imageTypeGroup.release());
418	}
419
420	return testGroup.release();
421}
422
423} // sparse
424} // vkt
425