1 /*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2022 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 vktImageTransfer.cpp
21 * \brief Tests for image transfers
22 *//*--------------------------------------------------------------------*/
23
24 #include "vktTestCase.hpp"
25
26 #include "vkImageUtil.hpp"
27 #include "vkBarrierUtil.hpp"
28 #include "vkTypeUtil.hpp"
29 #include "vkCmdUtil.hpp"
30 #include "vkStrUtil.hpp"
31 #include "vkBufferWithMemory.hpp"
32 #include "vkImageWithMemory.hpp"
33
34 #include "tcuTestLog.hpp"
35 #include "vktTestCase.hpp"
36 #include "tcuTextureUtil.hpp"
37 #include "tcuCommandLine.hpp"
38 #include "vktImageTestsUtil.hpp"
39 #include "vkRefUtil.hpp"
40 #include "deRandom.hpp"
41 #include "ycbcr/vktYCbCrUtil.hpp"
42
43 #include <vector>
44 #include <string>
45
46 using namespace vk;
47
48 namespace vkt
49 {
50 using namespace ycbcr;
51 namespace image
52 {
53 namespace
54 {
55
56 class TransferQueueCase : public vkt::TestCase
57 {
58 public:
59 struct TestParams
60 {
61 VkImageType imageType;
62 VkFormat imageFormat;
63 VkExtent3D dimensions; // .depth will be the number of layers for 2D images and the depth for 3D images.
64 };
65
66 TransferQueueCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TestParams& params);
~TransferQueueCase(void)67 virtual ~TransferQueueCase (void) {}
68
initPrograms(vk::SourceCollections &) const69 virtual void initPrograms (vk::SourceCollections&) const {}
70 virtual TestInstance* createInstance (Context& context) const;
71 virtual void checkSupport (Context& context) const;
72 private:
73 TestParams m_params;
74 };
75
76 class TransferQueueInstance : public vkt::TestInstance
77 {
78 public:
79 TransferQueueInstance (Context& context, const TransferQueueCase::TestParams& params);
~TransferQueueInstance(void)80 virtual ~TransferQueueInstance (void) {}
81
82 virtual tcu::TestStatus iterate (void);
83 private:
84 TransferQueueCase::TestParams m_params;
85 Move<VkCommandPool> m_cmdPool;
86 Move<VkCommandBuffer> m_cmdBuffer;
87 };
88
TransferQueueCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description,const TestParams & params)89 TransferQueueCase::TransferQueueCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TestParams& params)
90 : vkt::TestCase (testCtx, name, description)
91 , m_params (params)
92 {
93 }
94
createInstance(Context & context) const95 TestInstance* TransferQueueCase::createInstance (Context& context) const
96 {
97 return new TransferQueueInstance (context, m_params);
98 }
99
checkSupport(Context & context) const100 void TransferQueueCase::checkSupport (Context& context) const
101 {
102 const auto& vki = context.getInstanceInterface();
103 const auto physicalDevice = context.getPhysicalDevice();
104
105 VkImageFormatProperties formatProperties;
106 const auto result = vki.getPhysicalDeviceImageFormatProperties(physicalDevice, m_params.imageFormat, m_params.imageType, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, 0u, &formatProperties);
107 if (result != VK_SUCCESS)
108 {
109 if (result == VK_ERROR_FORMAT_NOT_SUPPORTED)
110 TCU_THROW(NotSupportedError, "Error: format " + de::toString(m_params.imageFormat) + " does not support the required features");
111 else
112 TCU_FAIL("vkGetPhysicalDeviceImageFormatProperties returned unexpected error");
113 }
114 }
115
TransferQueueInstance(Context & context,const TransferQueueCase::TestParams & params)116 TransferQueueInstance::TransferQueueInstance (Context& context, const TransferQueueCase::TestParams& params)
117 : vkt::TestInstance (context)
118 , m_params (params)
119 {
120 const auto& vk = context.getDeviceInterface();
121 const auto& device = context.getDevice();
122 const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex();
123
124 // Create command pool
125 m_cmdPool = createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex);
126
127 // Create command buffer
128 m_cmdBuffer = allocateCommandBuffer(vk, device, *m_cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
129 }
130
iterate(void)131 tcu::TestStatus TransferQueueInstance::iterate (void)
132 {
133 // Test every aspect supported by the image format.
134 const auto tcuFormat = mapVkFormat(m_params.imageFormat);
135
136 Allocator& allocator = m_context.getDefaultAllocator();
137 const auto& vk = m_context.getDeviceInterface();
138 const auto device = m_context.getDevice();
139 const auto queue = getDeviceQueue(m_context.getDeviceInterface(), device, m_context.getUniversalQueueFamilyIndex(), 0u);
140
141 const auto width = m_params.dimensions.width;
142 const auto height = m_params.dimensions.height;
143 const auto layers = m_params.imageType == vk::VK_IMAGE_TYPE_3D ? 1u : m_params.dimensions.depth;
144 const auto depth = m_params.imageType == vk::VK_IMAGE_TYPE_3D ? m_params.dimensions.depth : 1u;
145 const uint32_t pixelDataSize = tcuFormat.getPixelSize() * width * height * layers * depth;
146
147 const vk::VkBufferCreateInfo bufferCreateInfo =
148 {
149 vk::VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
150 DE_NULL,
151 0u, // flags
152 static_cast<VkDeviceSize>(pixelDataSize), // size
153 vk::VK_BUFFER_USAGE_TRANSFER_SRC_BIT | vk::VK_BUFFER_USAGE_TRANSFER_DST_BIT, // usage
154 vk::VK_SHARING_MODE_EXCLUSIVE, // sharingMode
155 0u, // queueFamilyCount
156 DE_NULL, // pQueueFamilyIndices
157 };
158
159 BufferWithMemory srcBuffer(vk, device, allocator, bufferCreateInfo, MemoryRequirement::HostVisible);
160 BufferWithMemory dstBuffer(vk, device, allocator, bufferCreateInfo, MemoryRequirement::HostVisible);
161
162 vk::VkExtent3D extent = { m_params.dimensions.width, m_params.dimensions.height, depth };
163 const vk::VkImageCreateInfo imageCreateInfo =
164 {
165 VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
166 DE_NULL, // const void* pNext;
167 0, // VkImageCreateFlags flags;
168 m_params.imageType, // VkImageType imageType;
169 m_params.imageFormat, // VkFormat format;
170 extent, // VkExtent3D extent;
171 1u, // deUint32 mipLevels;
172 layers, // deUint32 arraySize;
173 VK_SAMPLE_COUNT_1_BIT, // deUint32 samples;
174 VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
175 VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
176 VK_IMAGE_USAGE_TRANSFER_DST_BIT, // VkImageUsageFlags usage;
177 VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
178 0u, // deUint32 queueFamilyIndexCount;
179 (const deUint32*)DE_NULL, // const deUint32* pQueueFamilyIndices;
180 VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
181 };
182 Image image(vk, device, allocator, imageCreateInfo, MemoryRequirement::Any);
183
184 // Generate data for source buffer and copy it into buffer
185 std::vector<deUint8> generatedData (pixelDataSize);
186 de::Random randomGen (deInt32Hash((deUint32)m_params.imageFormat) ^
187 deInt32Hash((deUint32)m_params.imageType) ^
188 deInt32Hash((deUint32)m_params.dimensions.width) ^
189 deInt32Hash((deUint32)m_params.dimensions.height) ^
190 deInt32Hash((deUint32)m_params.dimensions.depth));
191 {
192 fillRandomNoNaN(&randomGen, generatedData.data(), (deUint32)generatedData.size(), m_params.imageFormat);
193 const Allocation& alloc = srcBuffer.getAllocation();
194 deMemcpy(alloc.getHostPtr(), generatedData.data(), generatedData.size());
195 }
196
197 beginCommandBuffer(vk, *m_cmdBuffer);
198 const VkImageSubresourceRange subresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, layers);
199 const VkImageMemoryBarrier imageBarrier = makeImageMemoryBarrier(0u, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, *image, subresourceRange);
200 vk.cmdPipelineBarrier(*m_cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 0, (const VkBufferMemoryBarrier*)DE_NULL, 1u, &imageBarrier);
201 // Copy buffer to image
202 {
203 const bool isCompressed = isCompressedFormat(m_params.imageFormat);
204 const deUint32 blockHeight = (isCompressed) ? getBlockHeight(m_params.imageFormat) : 1u;
205 deUint32 imageHeight = ((height + blockHeight - 1) / blockHeight) * blockHeight;
206
207 const vk::VkBufferImageCopy copyRegion =
208 {
209 0u, // VkDeviceSize bufferOffset;
210 0, // deUint32 bufferRowLength;
211 imageHeight, // deUint32 bufferImageHeight;
212 {
213 VK_IMAGE_ASPECT_COLOR_BIT, // VkImageAspectFlags aspect;
214 0, // deUint32 mipLevel;
215 0u, // deUint32 baseArrayLayer;
216 layers, // deUint32 layerCount;
217 }, // VkImageSubresourceLayers imageSubresource;
218 { 0, 0, 0 }, // VkOffset3D imageOffset;
219 extent // VkExtent3D imageExtent;
220 };
221
222 const VkImageMemoryBarrier postImageBarrier =
223 {
224 VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType;
225 DE_NULL, // const void* pNext;
226 VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags srcAccessMask;
227 VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags dstAccessMask;
228 VK_IMAGE_LAYOUT_GENERAL, // VkImageLayout oldLayout;
229 VK_IMAGE_LAYOUT_GENERAL, // VkImageLayout newLayout;
230 VK_QUEUE_FAMILY_IGNORED, // deUint32 srcQueueFamilyIndex;
231 VK_QUEUE_FAMILY_IGNORED, // deUint32 dstQueueFamilyIndex;
232 *image, // VkImage image;
233 { // VkImageSubresourceRange subresourceRange;
234 VK_IMAGE_ASPECT_COLOR_BIT, // VkImageAspectFlags aspect;
235 0u, // deUint32 baseMipLevel;
236 1, // deUint32 mipLevels;
237 0u, // deUint32 baseArraySlice;
238 layers, // deUint32 arraySize;
239 }
240 };
241
242 vk.cmdCopyBufferToImage(*m_cmdBuffer, *srcBuffer, *image, VK_IMAGE_LAYOUT_GENERAL, 1, ©Region);
243
244 vk.cmdPipelineBarrier(*m_cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 0, (const VkBufferMemoryBarrier*)DE_NULL, 1, &postImageBarrier);
245
246 vk.cmdCopyImageToBuffer(*m_cmdBuffer, *image, VK_IMAGE_LAYOUT_GENERAL, *dstBuffer, 1, ©Region);
247 }
248 endCommandBuffer(vk, *m_cmdBuffer);
249
250 submitCommandsAndWait(vk, device, queue, *m_cmdBuffer);
251
252 {
253 std::vector<deUint8> resultData(pixelDataSize);
254 const Allocation& alloc = dstBuffer.getAllocation();
255 deMemcpy(resultData.data(), alloc.getHostPtr(), resultData.size());
256
257 for (uint32_t i = 0; i < pixelDataSize; ++i) {
258 if (resultData[i] != generatedData[i]) {
259 return tcu::TestStatus::fail("Transfer queue test");
260 }
261 }
262 }
263
264 return tcu::TestStatus::pass("Pass");
265 }
266
267 } // anonymous namespace
268
getAspectFlags(tcu::TextureFormat format)269 VkImageAspectFlags getAspectFlags (tcu::TextureFormat format)
270 {
271 VkImageAspectFlags aspectFlag = 0;
272 aspectFlag |= (tcu::hasDepthComponent(format.order) ? VK_IMAGE_ASPECT_DEPTH_BIT : 0);
273 aspectFlag |= (tcu::hasStencilComponent(format.order) ? VK_IMAGE_ASPECT_STENCIL_BIT : 0);
274
275 if (!aspectFlag)
276 aspectFlag = VK_IMAGE_ASPECT_COLOR_BIT;
277
278 return aspectFlag;
279 }
280
createTransferQueueImageTests(tcu::TestContext & testCtx)281 tcu::TestCaseGroup* createTransferQueueImageTests (tcu::TestContext& testCtx)
282 {
283 de::MovePtr<tcu::TestCaseGroup> layoutTestGroup (new tcu::TestCaseGroup(testCtx, "queue_transfer", "Tests for transfering from buffer to image and back"));
284
285 struct
286 {
287 VkImageType type;
288 bool array;
289 const char* name;
290 const char* desc;
291 } imageClass[] =
292 {
293 { VK_IMAGE_TYPE_2D, false, "2d", "2D images" },
294 { VK_IMAGE_TYPE_2D, true, "2d_array", "2D images with multiple layers" },
295 { VK_IMAGE_TYPE_3D, false, "3d", "3D images" },
296 };
297
298 struct
299 {
300 VkExtent3D extent;
301 const char* name;
302 const char* desc;
303 } extents[] =
304 {
305 { {4u, 3u, 1u}, "4x3x1", "4x3x1 extent" },
306 { {16u, 15u, 1u}, "16x15x1", "16x15x1 extent" },
307 { {64u, 31u, 1u}, "64x31x1", "64x31x1 extent" },
308 { {4u, 3u, 2u}, "4x3x2", "4x3x2extent" },
309 { {16u, 15u, 16u}, "16x15x16", "16x15x16 extent" },
310 };
311
312 VkFormat testFormats[] =
313 {
314 VK_FORMAT_R4G4_UNORM_PACK8,
315 VK_FORMAT_R4G4B4A4_UNORM_PACK16,
316 VK_FORMAT_B4G4R4A4_UNORM_PACK16,
317 VK_FORMAT_R5G6B5_UNORM_PACK16,
318 VK_FORMAT_B5G6R5_UNORM_PACK16,
319 VK_FORMAT_R5G5B5A1_UNORM_PACK16,
320 VK_FORMAT_B5G5R5A1_UNORM_PACK16,
321 VK_FORMAT_A1R5G5B5_UNORM_PACK16,
322 VK_FORMAT_R8_UNORM,
323 VK_FORMAT_R8_SNORM,
324 VK_FORMAT_R8_USCALED,
325 VK_FORMAT_R8_SSCALED,
326 VK_FORMAT_R8_UINT,
327 VK_FORMAT_R8_SINT,
328 VK_FORMAT_R8_SRGB,
329 VK_FORMAT_R8G8_UNORM,
330 VK_FORMAT_R8G8_SNORM,
331 VK_FORMAT_R8G8_USCALED,
332 VK_FORMAT_R8G8_SSCALED,
333 VK_FORMAT_R8G8_UINT,
334 VK_FORMAT_R8G8_SINT,
335 VK_FORMAT_R8G8_SRGB,
336 VK_FORMAT_R8G8B8_UNORM,
337 VK_FORMAT_R8G8B8_SNORM,
338 VK_FORMAT_R8G8B8_USCALED,
339 VK_FORMAT_R8G8B8_SSCALED,
340 VK_FORMAT_R8G8B8_UINT,
341 VK_FORMAT_R8G8B8_SINT,
342 VK_FORMAT_R8G8B8_SRGB,
343 VK_FORMAT_B8G8R8_UNORM,
344 VK_FORMAT_B8G8R8_SNORM,
345 VK_FORMAT_B8G8R8_USCALED,
346 VK_FORMAT_B8G8R8_SSCALED,
347 VK_FORMAT_B8G8R8_UINT,
348 VK_FORMAT_B8G8R8_SINT,
349 VK_FORMAT_B8G8R8_SRGB,
350 VK_FORMAT_R8G8B8A8_UNORM,
351 VK_FORMAT_R8G8B8A8_SNORM,
352 VK_FORMAT_R8G8B8A8_USCALED,
353 VK_FORMAT_R8G8B8A8_SSCALED,
354 VK_FORMAT_R8G8B8A8_UINT,
355 VK_FORMAT_R8G8B8A8_SINT,
356 VK_FORMAT_R8G8B8A8_SRGB,
357 VK_FORMAT_B8G8R8A8_UNORM,
358 VK_FORMAT_B8G8R8A8_SNORM,
359 VK_FORMAT_B8G8R8A8_USCALED,
360 VK_FORMAT_B8G8R8A8_SSCALED,
361 VK_FORMAT_B8G8R8A8_UINT,
362 VK_FORMAT_B8G8R8A8_SINT,
363 VK_FORMAT_B8G8R8A8_SRGB,
364 VK_FORMAT_A8B8G8R8_UNORM_PACK32,
365 VK_FORMAT_A8B8G8R8_SNORM_PACK32,
366 VK_FORMAT_A8B8G8R8_USCALED_PACK32,
367 VK_FORMAT_A8B8G8R8_SSCALED_PACK32,
368 VK_FORMAT_A8B8G8R8_UINT_PACK32,
369 VK_FORMAT_A8B8G8R8_SINT_PACK32,
370 VK_FORMAT_A8B8G8R8_SRGB_PACK32,
371 VK_FORMAT_A2R10G10B10_UNORM_PACK32,
372 VK_FORMAT_A2R10G10B10_SNORM_PACK32,
373 VK_FORMAT_A2R10G10B10_USCALED_PACK32,
374 VK_FORMAT_A2R10G10B10_SSCALED_PACK32,
375 VK_FORMAT_A2R10G10B10_UINT_PACK32,
376 VK_FORMAT_A2R10G10B10_SINT_PACK32,
377 VK_FORMAT_A2B10G10R10_UNORM_PACK32,
378 VK_FORMAT_A2B10G10R10_SNORM_PACK32,
379 VK_FORMAT_A2B10G10R10_USCALED_PACK32,
380 VK_FORMAT_A2B10G10R10_SSCALED_PACK32,
381 VK_FORMAT_A2B10G10R10_UINT_PACK32,
382 VK_FORMAT_A2B10G10R10_SINT_PACK32,
383 VK_FORMAT_R16_UNORM,
384 VK_FORMAT_R16_SNORM,
385 VK_FORMAT_R16_USCALED,
386 VK_FORMAT_R16_SSCALED,
387 VK_FORMAT_R16_UINT,
388 VK_FORMAT_R16_SINT,
389 VK_FORMAT_R16_SFLOAT,
390 VK_FORMAT_R16G16_UNORM,
391 VK_FORMAT_R16G16_SNORM,
392 VK_FORMAT_R16G16_USCALED,
393 VK_FORMAT_R16G16_SSCALED,
394 VK_FORMAT_R16G16_UINT,
395 VK_FORMAT_R16G16_SINT,
396 VK_FORMAT_R16G16_SFLOAT,
397 VK_FORMAT_R16G16B16_UNORM,
398 VK_FORMAT_R16G16B16_SNORM,
399 VK_FORMAT_R16G16B16_USCALED,
400 VK_FORMAT_R16G16B16_SSCALED,
401 VK_FORMAT_R16G16B16_UINT,
402 VK_FORMAT_R16G16B16_SINT,
403 VK_FORMAT_R16G16B16_SFLOAT,
404 VK_FORMAT_R16G16B16A16_UNORM,
405 VK_FORMAT_R16G16B16A16_SNORM,
406 VK_FORMAT_R16G16B16A16_USCALED,
407 VK_FORMAT_R16G16B16A16_SSCALED,
408 VK_FORMAT_R16G16B16A16_UINT,
409 VK_FORMAT_R16G16B16A16_SINT,
410 VK_FORMAT_R16G16B16A16_SFLOAT,
411 VK_FORMAT_R32_UINT,
412 VK_FORMAT_R32_SINT,
413 VK_FORMAT_R32_SFLOAT,
414 VK_FORMAT_R32G32_UINT,
415 VK_FORMAT_R32G32_SINT,
416 VK_FORMAT_R32G32_SFLOAT,
417 VK_FORMAT_R32G32B32_UINT,
418 VK_FORMAT_R32G32B32_SINT,
419 VK_FORMAT_R32G32B32_SFLOAT,
420 VK_FORMAT_R32G32B32A32_UINT,
421 VK_FORMAT_R32G32B32A32_SINT,
422 VK_FORMAT_R32G32B32A32_SFLOAT,
423 VK_FORMAT_R64_UINT,
424 VK_FORMAT_R64_SINT,
425 VK_FORMAT_R64_SFLOAT,
426 VK_FORMAT_R64G64_UINT,
427 VK_FORMAT_R64G64_SINT,
428 VK_FORMAT_R64G64_SFLOAT,
429 VK_FORMAT_R64G64B64_UINT,
430 VK_FORMAT_R64G64B64_SINT,
431 VK_FORMAT_R64G64B64_SFLOAT,
432 VK_FORMAT_R64G64B64A64_UINT,
433 VK_FORMAT_R64G64B64A64_SINT,
434 VK_FORMAT_R64G64B64A64_SFLOAT,
435 };
436
437 for (int classIdx = 0; classIdx < DE_LENGTH_OF_ARRAY(imageClass); ++classIdx)
438 {
439 const auto& imgClass = imageClass[classIdx];
440 de::MovePtr<tcu::TestCaseGroup> classGroup (new tcu::TestCaseGroup(testCtx, imgClass.name, imgClass.desc));
441
442 for (int extentIdx = 0; extentIdx < DE_LENGTH_OF_ARRAY(extents); ++extentIdx)
443 {
444 const auto &extent = extents[extentIdx];
445 de::MovePtr<tcu::TestCaseGroup> mipGroup (new tcu::TestCaseGroup(testCtx, extent.name, extent.desc));
446
447 for (int formatIdx = 0; formatIdx < DE_LENGTH_OF_ARRAY(testFormats); ++formatIdx)
448 {
449 static const auto prefixLen = std::string("VK_FORMAT_").size();
450 const auto format = testFormats[formatIdx];
451 const auto fmtName = std::string(getFormatName(format));
452 const auto name = de::toLower(fmtName.substr(prefixLen)); // Remove VK_FORMAT_ prefix.
453 const auto desc = "Using format " + fmtName;
454
455 TransferQueueCase::TestParams params;
456 params.imageFormat = format;
457 params.imageType = imgClass.type;
458 params.dimensions = {extent.extent.width, extent.extent.height, extent.extent.depth};
459
460 mipGroup->addChild(new TransferQueueCase(testCtx, name, desc, params));
461 }
462
463 classGroup->addChild(mipGroup.release());
464 }
465
466 layoutTestGroup->addChild(classGroup.release());
467 }
468
469 return layoutTestGroup.release();
470 }
471
472 } // namespace image
473 } // namespace vkt
474