1 /*------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2022 The Khronos Group Inc.
6  * Copyright (c) 2022 Google LLC.
7  * Copyright (c) 2023 LunarG, Inc.
8  * Copyright (c) 2023 Nintendo
9  *
10  * Licensed under the Apache License, Version 2.0 (the "License");
11  * you may not use this file except in compliance with the License.
12  * You may obtain a copy of the License at
13  *
14  *      http://www.apache.org/licenses/LICENSE-2.0
15  *
16  * Unless required by applicable law or agreed to in writing, software
17  * distributed under the License is distributed on an "AS IS" BASIS,
18  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19  * See the License for the specific language governing permissions and
20  * limitations under the License.
21  *
22  *//*!
23  * \file
24  * \brief
25  *//*--------------------------------------------------------------------*/
26 
27 #include "vktPipelineImage2DViewOf3DTests.hpp"
28 #include "vkPipelineConstructionUtil.hpp"
29 #include "vktTestCase.hpp"
30 #include "vkImageUtil.hpp"
31 #include "vkPrograms.hpp"
32 #include "vkTypeUtil.hpp"
33 #include "vkObjUtil.hpp"
34 #include "vkCmdUtil.hpp"
35 #include "vkBuilderUtil.hpp"
36 #include "vkImageWithMemory.hpp"
37 #include "vkBufferWithMemory.hpp"
38 #include "vkBarrierUtil.hpp"
39 #include "tcuTexture.hpp"
40 #include "tcuPlatform.hpp"
41 #include "tcuImageCompare.hpp"
42 #include "deMemory.h"
43 
44 #include <sstream>
45 #include <vector>
46 
47 namespace vkt
48 {
49 namespace pipeline
50 {
51 
52 using namespace vk;
53 using de::MovePtr;
54 
55 namespace
56 {
57 enum ImageAccessType
58 {
59     StorageImage = 0,
60     Sampler,
61     CombinedImageSampler
62 };
63 
64 enum TestType
65 {
66     Compute,
67     Fragment
68 };
69 
70 struct TestParameters
71 {
72     tcu::IVec3 imageSize;
73     uint32_t mipLevel;
74     int32_t layerNdx;
75     ImageAccessType imageType;
76     TestType testType;
77     VkFormat imageFormat;
78     PipelineConstructionType pipelineConstructionType;
79 };
80 
computeMipLevelDimension(int32_t baseLevelDimension,uint32_t mipLevel)81 inline int32_t computeMipLevelDimension(int32_t baseLevelDimension, uint32_t mipLevel)
82 {
83     return de::max(baseLevelDimension >> mipLevel, 1);
84 }
85 
computeMipLevelSize(tcu::IVec3 baseLevelSize,uint32_t mipLevel)86 tcu::IVec3 computeMipLevelSize(tcu::IVec3 baseLevelSize, uint32_t mipLevel)
87 {
88     int32_t width  = computeMipLevelDimension(baseLevelSize.x(), mipLevel);
89     int32_t height = computeMipLevelDimension(baseLevelSize.y(), mipLevel);
90     int32_t depth  = computeMipLevelDimension(baseLevelSize.z(), mipLevel);
91     return tcu::IVec3(width, height, depth);
92 }
93 
copyImageLayerToBuffer(const DeviceInterface & vk,VkCommandBuffer cmdBuffer,VkImage image,VkBuffer buffer,tcu::IVec2 size,VkAccessFlags srcAccessMask,VkImageLayout oldLayout,uint32_t layerToCopy,uint32_t mipLevel)94 void copyImageLayerToBuffer(const DeviceInterface &vk, VkCommandBuffer cmdBuffer, VkImage image, VkBuffer buffer,
95                             tcu::IVec2 size, VkAccessFlags srcAccessMask, VkImageLayout oldLayout, uint32_t layerToCopy,
96                             uint32_t mipLevel)
97 {
98     const VkImageSubresourceRange subresourceRange =
99         makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, mipLevel, 1u, 0, 1u);
100     const VkImageMemoryBarrier imageBarrier = {
101         VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType            sType
102         DE_NULL,                                // const void*                pNext
103         srcAccessMask,                          // VkAccessFlags            srcAccessMask
104         VK_ACCESS_TRANSFER_READ_BIT,            // VkAccessFlags            dstAccessMask
105         oldLayout,                              // VkImageLayout            oldLayout
106         VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,   // VkImageLayout            newLayout
107         VK_QUEUE_FAMILY_IGNORED,                // uint32_t                    srcQueueFamilyIndex
108         VK_QUEUE_FAMILY_IGNORED,                // uint32_t                    destQueueFamilyIndex
109         image,                                  // VkImage                    image
110         subresourceRange                        // VkImageSubresourceRange    subresourceRange
111     };
112 
113     vk.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 0u,
114                           DE_NULL, 0u, DE_NULL, 1u, &imageBarrier);
115 
116     const VkImageSubresourceLayers subresource = {
117         subresourceRange.aspectMask, // VkImageAspectFlags    aspectMask
118         mipLevel,                    // uint32_t                mipLevel
119         0u,                          // uint32_t                baseArrayLayer
120         1u,                          // uint32_t                layerCount
121     };
122 
123     const VkBufferImageCopy region = {
124         0ull,                                 // VkDeviceSize                    bufferOffset
125         0u,                                   // uint32_t                        bufferRowLength
126         0u,                                   // uint32_t                        bufferImageHeight
127         subresource,                          // VkImageSubresourceLayers        imageSubresource
128         makeOffset3D(0, 0, (int)layerToCopy), // VkOffset3D                    imageOffset
129         makeExtent3D(size.x(), size.y(), 1u)  // VkExtent3D                    imageExtent
130     };
131 
132     vk.cmdCopyImageToBuffer(cmdBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, buffer, 1u, &region);
133 
134     const VkBufferMemoryBarrier bufferBarrier = {
135         VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, // VkStructureType    sType
136         DE_NULL,                                 // const void*        pNext
137         VK_ACCESS_TRANSFER_WRITE_BIT,            // VkAccessFlags    srcAccessMask
138         VK_ACCESS_HOST_READ_BIT,                 // VkAccessFlags    dstAccessMask
139         VK_QUEUE_FAMILY_IGNORED,                 // uint32_t            srcQueueFamilyIndex
140         VK_QUEUE_FAMILY_IGNORED,                 // uint32_t            dstQueueFamilyIndex
141         buffer,                                  // VkBuffer            buffer
142         0ull,                                    // VkDeviceSize        offset
143         VK_WHOLE_SIZE                            // VkDeviceSize        size
144     };
145 
146     vk.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 0u, DE_NULL, 1u,
147                           &bufferBarrier, 0u, DE_NULL);
148 }
149 
150 // Draws a chess pattern to the given 'layer' (z-dimension) of the 'image'. Other layers will be cleared to white.
fillImage(const tcu::PixelBufferAccess & image,const int layer)151 void fillImage(const tcu::PixelBufferAccess &image, const int layer)
152 {
153     const tcu::Vec4 clearColor = tcu::Vec4(1); // White clear color.
154     for (int z = 0; z < image.getSize().z(); ++z)
155         for (int y = 0; y < image.getSize().y(); ++y)
156             for (int x = 0; x < image.getSize().x(); ++x)
157             {
158                 if (z == layer)
159                 {
160                     const float c         = (float)((x + y) & 1);
161                     const tcu::Vec4 color = tcu::Vec4(c, c, c, 1.0f);
162                     image.setPixel(color, x, y, z);
163                 }
164                 else
165                 {
166                     image.setPixel(clearColor, x, y, z);
167                 }
168             }
169 }
170 
171 class Image2DView3DImageInstance : public vkt::TestInstance
172 {
173 public:
Image2DView3DImageInstance(Context & context,const TestParameters testParameters)174     Image2DView3DImageInstance(Context &context, const TestParameters testParameters)
175         : vkt::TestInstance(context)
176         , m_testParameters(testParameters)
177     {
178     }
179 
180     tcu::TestStatus iterate(void);
181 
182 private:
183     void runComputePipeline(const VkDescriptorSet &descriptorSet, const VkDescriptorSetLayout descriptorSetLayout,
184                             tcu::IVec3 &testMipLevelSize, VkCommandBuffer cmdBuffer, VkImage image,
185                             VkBuffer outputBuffer);
186 
187     void runGraphicsPipeline(const VkDescriptorSet &descriptorSet, const VkDescriptorSetLayout descriptorSetLayout,
188                              tcu::IVec3 &testMipLevelSize, VkCommandBuffer cmdBuffer, VkImage image,
189                              VkBuffer outputBuffer);
190     const TestParameters m_testParameters;
191 };
192 
runComputePipeline(const VkDescriptorSet & descriptorSet,const VkDescriptorSetLayout descriptorSetLayout,tcu::IVec3 & testMipLevelSize,VkCommandBuffer cmdBuffer,VkImage image,VkBuffer outputBuffer)193 void Image2DView3DImageInstance::runComputePipeline(const VkDescriptorSet &descriptorSet,
194                                                     const VkDescriptorSetLayout descriptorSetLayout,
195                                                     tcu::IVec3 &testMipLevelSize, VkCommandBuffer cmdBuffer,
196                                                     VkImage image, VkBuffer outputBuffer)
197 {
198     const DeviceInterface &vk = m_context.getDeviceInterface();
199     const VkDevice device     = m_context.getDevice();
200     const VkQueue queue       = m_context.getUniversalQueue();
201     const bool useSampler     = m_testParameters.imageType != StorageImage;
202 
203     const Unique<VkShaderModule> shaderModule(
204         createShaderModule(vk, device, m_context.getBinaryCollection().get("comp"), 0u));
205     const Unique<VkPipelineLayout> pipelineLayout(makePipelineLayout(vk, device, descriptorSetLayout));
206     const Unique<VkPipeline> pipeline(makeComputePipeline(vk, device, *pipelineLayout, *shaderModule));
207 
208     vk.cmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
209     vk.cmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0u, 1u, &descriptorSet, 0u,
210                              DE_NULL);
211     vk.cmdDispatch(cmdBuffer, testMipLevelSize.x(), testMipLevelSize.y(), 1u);
212 
213     // Copy the result image to a buffer.
214     copyImageLayerToBuffer(vk, cmdBuffer, image, outputBuffer, testMipLevelSize.xy(), VK_ACCESS_SHADER_WRITE_BIT,
215                            VK_IMAGE_LAYOUT_GENERAL, useSampler ? 0u : m_testParameters.layerNdx,
216                            useSampler ? 0u : m_testParameters.mipLevel);
217 
218     endCommandBuffer(vk, cmdBuffer);
219 
220     // Wait for completion.
221     submitCommandsAndWait(vk, device, queue, cmdBuffer);
222 }
223 
runGraphicsPipeline(const VkDescriptorSet & descriptorSet,const VkDescriptorSetLayout descriptorSetLayout,tcu::IVec3 & testMipLevelSize,VkCommandBuffer cmdBuffer,VkImage image,VkBuffer outputBuffer)224 void Image2DView3DImageInstance::runGraphicsPipeline(const VkDescriptorSet &descriptorSet,
225                                                      const VkDescriptorSetLayout descriptorSetLayout,
226                                                      tcu::IVec3 &testMipLevelSize, VkCommandBuffer cmdBuffer,
227                                                      VkImage image, VkBuffer outputBuffer)
228 {
229     const InstanceInterface &vki          = m_context.getInstanceInterface();
230     const DeviceInterface &vk             = m_context.getDeviceInterface();
231     const VkPhysicalDevice physicalDevice = m_context.getPhysicalDevice();
232     const VkDevice device                 = m_context.getDevice();
233     const VkQueue queue                   = m_context.getUniversalQueue();
234     const bool useSampler                 = m_testParameters.imageType != StorageImage;
235 
236     const ShaderWrapper vertShader(ShaderWrapper(vk, device, m_context.getBinaryCollection().get("vert"), 0u));
237     const ShaderWrapper fragShader(ShaderWrapper(vk, device, m_context.getBinaryCollection().get("frag"), 0u));
238     const PipelineLayoutWrapper pipelineLayout(m_testParameters.pipelineConstructionType, vk, device,
239                                                descriptorSetLayout);
240     RenderPassWrapper renderPass(m_testParameters.pipelineConstructionType, vk, device);
241     const std::vector<VkViewport> viewport = {
242         makeViewport(m_testParameters.imageSize.x(), m_testParameters.imageSize.y())};
243     const std::vector<VkRect2D> scissor = {makeRect2D(m_testParameters.imageSize.x(), m_testParameters.imageSize.y())};
244 
245     const VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCreateInfo = {
246         VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, // VkStructureType                            sType
247         DE_NULL,                                                     // const void*                                pNext
248         0u,                                                          // VkPipelineInputAssemblyStateCreateFlags    flags
249         VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN, // VkPrimitiveTopology                        topology
250         VK_FALSE                            // VkBool32                                    primitiveRestartEnable
251     };
252 
253     const VkVertexInputBindingDescription vertexInputBindingDescription = {
254         0u,                          // uint32_t                binding
255         sizeof(tcu::Vec4),           // uint32_t                stride
256         VK_VERTEX_INPUT_RATE_VERTEX, // VkVertexInputRate    inputRate
257     };
258 
259     const VkVertexInputAttributeDescription vertexInputAttributeDescription = {
260         0u,                            // uint32_t        location
261         0u,                            // uint32_t        binding
262         VK_FORMAT_R32G32B32A32_SFLOAT, // VkFormat        format
263         0u                             // uint32_t        offset
264     };
265 
266     const VkPipelineVertexInputStateCreateInfo vertexInputStateCreateInfoDefault = {
267         VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, // VkStructureType                                sType
268         DE_NULL,                                  // const void*                                    pNext
269         (VkPipelineVertexInputStateCreateFlags)0, // VkPipelineVertexInputStateCreateFlags        flags
270         1u,                             // uint32_t                                        vertexBindingDescriptionCount
271         &vertexInputBindingDescription, // const VkVertexInputBindingDescription*        pVertexBindingDescriptions
272         1u, // uint32_t                                        vertexAttributeDescriptionCount
273         &vertexInputAttributeDescription // const VkVertexInputAttributeDescription*        pVertexAttributeDescriptions
274     };
275 
276     vk::GraphicsPipelineWrapper graphicsPipeline(vki, vk, physicalDevice, device, m_context.getDeviceExtensions(),
277                                                  m_testParameters.pipelineConstructionType, 0u);
278     graphicsPipeline.setMonolithicPipelineLayout(pipelineLayout)
279         .setDefaultDepthStencilState()
280         .setDefaultRasterizationState()
281         .setDefaultMultisampleState()
282         .setupVertexInputState(&vertexInputStateCreateInfoDefault, &inputAssemblyStateCreateInfo)
283         .setupPreRasterizationShaderState(viewport, scissor, pipelineLayout, *renderPass, 0u, vertShader)
284         .setupFragmentShaderState(pipelineLayout, *renderPass, 0u, fragShader)
285         .setupFragmentOutputState(*renderPass, 0u)
286         .buildPipeline();
287 
288     renderPass.createFramebuffer(vk, device, 0u, DE_NULL, DE_NULL, testMipLevelSize.x(), testMipLevelSize.y());
289 
290     // Create vertex buffer and fill it with full screen quad.
291     const std::vector<tcu::Vec4> vertexData = {
292         {-1, -1, 1, 1},
293         {1, -1, 1, 1},
294         {1, 1, 1, 1},
295         {-1, 1, 1, 1},
296     };
297     size_t vertexBufferSize = sizeof(tcu::Vec4) * vertexData.size();
298     BufferWithMemory vertexBuffer(vk, device, m_context.getDefaultAllocator(),
299                                   makeBufferCreateInfo(vertexBufferSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT),
300                                   MemoryRequirement::HostVisible);
301     deMemcpy(vertexBuffer.getAllocation().getHostPtr(), vertexData.data(), vertexBufferSize);
302     flushAlloc(vk, device, vertexBuffer.getAllocation());
303 
304     VkDeviceSize vertexBufferOffset = 0;
305     vk.cmdBindVertexBuffers(cmdBuffer, 0, 1, &*vertexBuffer, &vertexBufferOffset);
306 
307     graphicsPipeline.bind(cmdBuffer);
308     vk.cmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0u, 1u, &descriptorSet, 0u,
309                              DE_NULL);
310 
311     renderPass.begin(vk, cmdBuffer, makeRect2D(testMipLevelSize.xy()));
312     vk.cmdDraw(cmdBuffer, 4, 1, 0, 0);
313     renderPass.end(vk, cmdBuffer);
314 
315     // Copy the result image to a buffer.
316     copyImageLayerToBuffer(vk, cmdBuffer, image, outputBuffer, testMipLevelSize.xy(), VK_ACCESS_SHADER_WRITE_BIT,
317                            VK_IMAGE_LAYOUT_GENERAL, useSampler ? 0u : m_testParameters.layerNdx,
318                            useSampler ? 0u : m_testParameters.mipLevel);
319 
320     endCommandBuffer(vk, cmdBuffer);
321 
322     // Wait for completion.
323     submitCommandsAndWait(vk, device, queue, cmdBuffer);
324 }
325 
iterate(void)326 tcu::TestStatus Image2DView3DImageInstance::iterate(void)
327 {
328     const DeviceInterface &vk              = m_context.getDeviceInterface();
329     const VkDevice device                  = m_context.getDevice();
330     const VkQueue queue                    = m_context.getUniversalQueue();
331     const uint32_t queueFamilyIndex        = m_context.getUniversalQueueFamilyIndex();
332     Allocator &allocator                   = m_context.getDefaultAllocator();
333     tcu::IVec3 imageSize                   = m_testParameters.imageSize;
334     const bool useSampler                  = m_testParameters.imageType != StorageImage;
335     const tcu::TextureFormat textureFormat = mapVkFormat(m_testParameters.imageFormat);
336     const uint32_t mipLevelCount           = 3;
337 
338     tcu::IVec3 testMipLevelSize = computeMipLevelSize(m_testParameters.imageSize, m_testParameters.mipLevel);
339     uint32_t bufferSize =
340         testMipLevelSize.x() * testMipLevelSize.y() * testMipLevelSize.z() * textureFormat.getPixelSize();
341     const BufferWithMemory outputBuffer(vk, device, allocator,
342                                         makeBufferCreateInfo(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT),
343                                         MemoryRequirement::HostVisible);
344 
345     // Input image is used with sampler cases only.
346     de::MovePtr<BufferWithMemory> inputImageBuffer;
347 
348     // Upload the test image data for sampler cases.
349     if (useSampler)
350     {
351         // Initialize the input image's mip level and fill the target layer with a chess pattern, others will be white.
352         tcu::TextureLevel inputImageMipLevel(textureFormat, testMipLevelSize.x(), testMipLevelSize.y(),
353                                              testMipLevelSize.z());
354         fillImage(inputImageMipLevel.getAccess(), m_testParameters.layerNdx);
355 
356         // Create a buffer to upload the image.
357         const VkBufferCreateInfo bufferCreateInfo =
358             makeBufferCreateInfo(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT);
359         inputImageBuffer = de::MovePtr<BufferWithMemory>(
360             new BufferWithMemory(vk, device, allocator, bufferCreateInfo, MemoryRequirement::HostVisible));
361 
362         // Upload target mip level to the input buffer.
363         deMemcpy(inputImageBuffer->getAllocation().getHostPtr(), inputImageMipLevel.getAccess().getDataPtr(),
364                  bufferSize);
365         flushAlloc(vk, device, inputImageBuffer->getAllocation());
366     }
367 
368     // Create the test image: sampled image or storage image, depending on the test type.
369     const VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
370                                     (useSampler ? VK_IMAGE_USAGE_SAMPLED_BIT : VK_IMAGE_USAGE_STORAGE_BIT);
371     const VkImageCreateInfo imageCreateInfo = {
372         VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,                       // VkStructureType            sType
373         DE_NULL,                                                   // const void*                pNext
374         VK_IMAGE_CREATE_2D_VIEW_COMPATIBLE_BIT_EXT,                // VkImageCreateFlags        flags
375         VK_IMAGE_TYPE_3D,                                          // VkImageType                imageType
376         m_testParameters.imageFormat,                              // VkFormat                    format
377         makeExtent3D(imageSize.x(), imageSize.y(), imageSize.z()), // VkExtent3D                extent
378         (uint32_t)mipLevelCount,                                   // uint32_t                    mipLevels
379         1u,                                                        // uint32_t                    arrayLayers
380         VK_SAMPLE_COUNT_1_BIT,                                     // VkSampleCountFlagBits    samples
381         VK_IMAGE_TILING_OPTIMAL,                                   // VkImageTiling            tiling
382         usage,                                                     // VkImageUsageFlags        usage
383         VK_SHARING_MODE_EXCLUSIVE,                                 // VkSharingMode            sharingMode
384         0u,                                                        // uint32_t                    queueFamilyIndexCount
385         DE_NULL,                                                   // const uint32_t*            pQueueFamilyIndices
386         VK_IMAGE_LAYOUT_UNDEFINED,                                 // VkImageLayout            initialLayout
387     };
388     ImageWithMemory testImage(vk, device, allocator, imageCreateInfo, MemoryRequirement::Any);
389 
390     // Make an image view covering one of the mip levels.
391     const VkImageSubresourceRange subresourceRange = makeImageSubresourceRange(
392         VK_IMAGE_ASPECT_COLOR_BIT, m_testParameters.mipLevel, 1u, m_testParameters.layerNdx, 1u);
393     const Unique<VkImageView> imageView(
394         makeImageView(vk, device, *testImage, VK_IMAGE_VIEW_TYPE_2D, m_testParameters.imageFormat, subresourceRange));
395 
396     // resultImage is used in sampler / combined image sampler tests to verify the sampled image.
397     MovePtr<ImageWithMemory> resultImage;
398     Move<VkImageView> resultImageView;
399     Move<VkSampler> sampler;
400     if (useSampler)
401     {
402         const VkImageCreateInfo resultImageCreateInfo = {
403             VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,                         // VkStructureType            sType
404             DE_NULL,                                                     // const void*                pNext
405             0U,                                                          // VkImageCreateFlags        flags
406             VK_IMAGE_TYPE_2D,                                            // VkImageType                imageType
407             m_testParameters.imageFormat,                                // VkFormat                    format
408             makeExtent3D(testMipLevelSize.x(), testMipLevelSize.y(), 1), // VkExtent3D                extent
409             1u,                                                          // uint32_t                    mipLevels
410             1u,                                                          // uint32_t                    arrayLayers
411             VK_SAMPLE_COUNT_1_BIT,                                       // VkSampleCountFlagBits    samples
412             VK_IMAGE_TILING_OPTIMAL,                                     // VkImageTiling            tiling
413             VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
414                 VK_IMAGE_USAGE_TRANSFER_DST_BIT, // VkImageUsageFlags        usage
415             VK_SHARING_MODE_EXCLUSIVE,           // VkSharingMode            sharingMode
416             0u,                                  // uint32_t                    queueFamilyIndexCount
417             DE_NULL,                             // const uint32_t*            pQueueFamilyIndices
418             VK_IMAGE_LAYOUT_UNDEFINED,           // VkImageLayout            initialLayout
419         };
420 
421         resultImage = MovePtr<ImageWithMemory>(
422             new ImageWithMemory(vk, device, allocator, resultImageCreateInfo, MemoryRequirement::Any));
423         const VkImageSubresourceRange resultImgSubresourceRange =
424             makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
425         resultImageView = makeImageView(vk, device, **resultImage, VK_IMAGE_VIEW_TYPE_2D, m_testParameters.imageFormat,
426                                         resultImgSubresourceRange);
427 
428         const VkSamplerCreateInfo samplerCreateInfo = {
429             VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,   // VkStructureType            sType
430             DE_NULL,                                 // const void*                pNext
431             (VkSamplerCreateFlags)0,                 // VkSamplerCreateFlags        flags
432             VK_FILTER_NEAREST,                       // VkFilter                    magFilter
433             VK_FILTER_NEAREST,                       // VkFilter                    minFilter
434             VK_SAMPLER_MIPMAP_MODE_NEAREST,          // VkSamplerMipmapMode        mipmapMode
435             VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,   // VkSamplerAddressMode        addressModeU
436             VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,   // VkSamplerAddressMode        addressModeV
437             VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,   // VkSamplerAddressMode        addressModeW
438             0.0f,                                    // float                    mipLodBias
439             VK_FALSE,                                // VkBool32                    anisotropyEnable
440             1.0f,                                    // float                    maxAnisotropy
441             VK_FALSE,                                // VkBool32                    compareEnable
442             VK_COMPARE_OP_ALWAYS,                    // VkCompareOp                compareOp
443             0.0f,                                    // float                    minLod
444             1.0f,                                    // float                    maxLod
445             VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, // VkBorderColor            borderColor
446             VK_FALSE,                                // VkBool32                    unnormalizedCoordinates
447         };
448         sampler = createSampler(vk, device, &samplerCreateInfo);
449     }
450 
451     // Create the descriptor set.
452     DescriptorSetLayoutBuilder descriptorSetLayoutBuilder;
453     DescriptorPoolBuilder descriptorPoolBuilder;
454 
455     VkShaderStageFlags shaderStage =
456         m_testParameters.testType == Compute ? VK_SHADER_STAGE_COMPUTE_BIT : VK_SHADER_STAGE_FRAGMENT_BIT;
457     VkPipelineStageFlags pipelineStage = m_testParameters.testType == Compute ? VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT :
458                                                                                 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
459     switch (m_testParameters.imageType)
460     {
461     case StorageImage:
462         descriptorSetLayoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, shaderStage);
463         descriptorPoolBuilder.addType(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE);
464         break;
465     case Sampler:
466         descriptorSetLayoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, shaderStage);
467         descriptorSetLayoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_SAMPLER, shaderStage);
468         descriptorSetLayoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, shaderStage);
469         descriptorPoolBuilder.addType(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE);
470         descriptorPoolBuilder.addType(VK_DESCRIPTOR_TYPE_SAMPLER);
471         descriptorPoolBuilder.addType(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE);
472         break;
473     case CombinedImageSampler:
474         descriptorSetLayoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, shaderStage);
475         descriptorSetLayoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, shaderStage);
476         descriptorPoolBuilder.addType(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
477         descriptorPoolBuilder.addType(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE);
478         break;
479     default:
480         TCU_THROW(InternalError, "Unimplemented testImage type.");
481     }
482 
483     if (useSampler)
484     {
485         // Clear the result image.
486         clearColorImage(vk, device, queue, m_context.getUniversalQueueFamilyIndex(), **resultImage,
487                         tcu::Vec4(0, 0, 0, 1), VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, pipelineStage, 0u,
488                         1u);
489     }
490     else
491     {
492         // Clear the test image.
493         clearColorImage(vk, device, queue, m_context.getUniversalQueueFamilyIndex(), testImage.get(),
494                         tcu::Vec4(0, 0, 0, 1), VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, pipelineStage, 0u,
495                         1u, 0u, mipLevelCount);
496     }
497 
498     // Prepare the command buffer.
499     const Unique<VkCommandPool> cmdPool(makeCommandPool(vk, device, queueFamilyIndex));
500     const Unique<VkCommandBuffer> cmdBuffer(
501         allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
502 
503     // Start recording commands.
504     beginCommandBuffer(vk, *cmdBuffer);
505 
506     if (useSampler)
507     {
508         // Copy the input image to the target mip level.
509         std::vector<VkBufferImageCopy> copies;
510         copies.push_back(makeBufferImageCopy(
511             makeExtent3D(testMipLevelSize),
512             makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, m_testParameters.mipLevel, 0, 1)));
513         copyBufferToImage(vk, *cmdBuffer, **inputImageBuffer, bufferSize, copies, VK_IMAGE_ASPECT_COLOR_BIT,
514                           mipLevelCount, 1u, *testImage, VK_IMAGE_LAYOUT_GENERAL, pipelineStage);
515     }
516 
517     const Move<VkDescriptorSetLayout> descriptorSetLayout(descriptorSetLayoutBuilder.build(vk, device));
518     const Move<VkDescriptorPool> descriptorPool(
519         descriptorPoolBuilder.build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
520     const Unique<VkDescriptorSet> descriptorSet(makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
521     const VkDescriptorImageInfo testImageDescriptorInfo =
522         makeDescriptorImageInfo(*sampler, *imageView, VK_IMAGE_LAYOUT_GENERAL);
523 
524     // Write descriptor update.
525     {
526         DescriptorSetUpdateBuilder descriptorSetUpdateBuilder;
527         uint32_t bindingIdx = 0;
528 
529         switch (m_testParameters.imageType)
530         {
531         case StorageImage:
532             descriptorSetUpdateBuilder.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u),
533                                                    VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &testImageDescriptorInfo);
534             break;
535         case Sampler:
536             descriptorSetUpdateBuilder.writeSingle(*descriptorSet,
537                                                    DescriptorSetUpdateBuilder::Location::binding(bindingIdx++),
538                                                    VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, &testImageDescriptorInfo);
539             descriptorSetUpdateBuilder.writeSingle(*descriptorSet,
540                                                    DescriptorSetUpdateBuilder::Location::binding(bindingIdx++),
541                                                    VK_DESCRIPTOR_TYPE_SAMPLER, &testImageDescriptorInfo);
542             break;
543         case CombinedImageSampler:
544             descriptorSetUpdateBuilder.writeSingle(*descriptorSet,
545                                                    DescriptorSetUpdateBuilder::Location::binding(bindingIdx++),
546                                                    VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &testImageDescriptorInfo);
547             break;
548         }
549 
550         if (useSampler)
551         {
552             const VkDescriptorImageInfo resultImageDescriptorInfo =
553                 makeDescriptorImageInfo(DE_NULL, *resultImageView, VK_IMAGE_LAYOUT_GENERAL);
554             descriptorSetUpdateBuilder.writeSingle(*descriptorSet,
555                                                    DescriptorSetUpdateBuilder::Location::binding(bindingIdx),
556                                                    VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &resultImageDescriptorInfo);
557         }
558 
559         descriptorSetUpdateBuilder.update(vk, device);
560     }
561 
562     if (m_testParameters.testType == Compute)
563         runComputePipeline(*descriptorSet, *descriptorSetLayout, testMipLevelSize, *cmdBuffer,
564                            useSampler ? **resultImage : *testImage, *outputBuffer);
565     else
566         runGraphicsPipeline(*descriptorSet, *descriptorSetLayout, testMipLevelSize, *cmdBuffer,
567                             useSampler ? **resultImage : *testImage, *outputBuffer);
568 
569     // Validate the results.
570     {
571         // Create a reference image.
572         // The reference image has always a depth of 1, because it will be compared to the 2D result image (sampler cases) or to a single layer of a 3D image.
573         tcu::TextureLevel referenceImage(textureFormat, testMipLevelSize.x(), testMipLevelSize.y(), 1u);
574         fillImage(referenceImage.getAccess(), 0u);
575 
576         const Allocation &outputBufferAllocation = outputBuffer.getAllocation();
577         invalidateAlloc(vk, device, outputBufferAllocation);
578 
579         const uint32_t *bufferPtr = static_cast<uint32_t *>(outputBufferAllocation.getHostPtr());
580         tcu::ConstPixelBufferAccess pixelBufferAccess(mapVkFormat(VK_FORMAT_R8G8B8A8_UNORM), testMipLevelSize.x(),
581                                                       testMipLevelSize.y(), 1u, bufferPtr);
582 
583         if (!tcu::floatThresholdCompare(m_context.getTestContext().getLog(), "Result", "Result comparison",
584                                         referenceImage, pixelBufferAccess, tcu::Vec4(0.01f), tcu::COMPARE_LOG_ON_ERROR))
585             return tcu::TestStatus::fail("Pixel comparison failed.");
586     }
587 
588     return tcu::TestStatus::pass("pass");
589 }
590 
591 class ComputeImage2DView3DImageTest : public vkt::TestCase
592 {
593 public:
ComputeImage2DView3DImageTest(tcu::TestContext & testContext,const char * name,const TestParameters & testParameters)594     ComputeImage2DView3DImageTest(tcu::TestContext &testContext, const char *name, const TestParameters &testParameters)
595         : vkt::TestCase(testContext, name)
596         , m_testParameters(testParameters)
597     {
598     }
~ComputeImage2DView3DImageTest(void)599     virtual ~ComputeImage2DView3DImageTest(void)
600     {
601     }
602 
603     virtual void initPrograms(SourceCollections &sourceCollections) const;
604     virtual void checkSupport(Context &context) const;
605     virtual TestInstance *createInstance(Context &context) const;
606 
607 private:
608     const TestParameters m_testParameters;
609 };
610 
checkSupport(Context & context) const611 void ComputeImage2DView3DImageTest::checkSupport(Context &context) const
612 {
613     DE_ASSERT(m_testParameters.pipelineConstructionType == PIPELINE_CONSTRUCTION_TYPE_MONOLITHIC);
614 
615     if (!context.isDeviceFunctionalitySupported("VK_EXT_image_2d_view_of_3d"))
616         TCU_THROW(NotSupportedError, "VK_EXT_image_2d_view_of_3d functionality not supported.");
617 
618     if (!context.getImage2DViewOf3DFeaturesEXT().image2DViewOf3D)
619         TCU_THROW(NotSupportedError, "image2DViewOf3D not supported.");
620 
621     if (m_testParameters.imageType != StorageImage && !context.getImage2DViewOf3DFeaturesEXT().sampler2DViewOf3D)
622         TCU_THROW(NotSupportedError, "sampler2DViewOf3D not supported.");
623 }
624 
initPrograms(SourceCollections & sourceCollections) const625 void ComputeImage2DView3DImageTest::initPrograms(SourceCollections &sourceCollections) const
626 {
627     std::ostringstream src;
628     tcu::IVec3 mipLevelSize = computeMipLevelSize(m_testParameters.imageSize, m_testParameters.mipLevel);
629     if (m_testParameters.imageType == StorageImage)
630     {
631         src << "#version 450 core\n"
632             << "layout (local_size_x = 1, local_size_y = 1) in;\n"
633             << "layout (binding = 0, rgba8) writeonly uniform highp image2D storageImage;\n"
634             << "void main (void) {\n"
635             << "    ivec2 uv = ivec2(gl_GlobalInvocationID.xy);\n"
636             << "    float c = float((uv.x + uv.y) & 1);\n"
637             << "    vec4 color = vec4(c, c, c, 1.0);\n"
638             << "    imageStore(storageImage, uv, color);\n"
639             << "}\n";
640     }
641     else if (m_testParameters.imageType == Sampler)
642     {
643         src << "#version 450 core\n"
644             << "layout (local_size_x = 1, local_size_y = 1) in;\n"
645             << "layout (set=0, binding = 0) uniform texture2D image;\n"
646             << "layout (set=0, binding = 1) uniform sampler samp;\n"
647             << "layout (rgba8, set=0, binding = 2) writeonly uniform highp image2D verifyImage;\n"
648             << "void main (void) {\n"
649             << "    ivec2 uv = ivec2(gl_GlobalInvocationID.xy);\n"
650             << "    vec2 texCoord = vec2(gl_GlobalInvocationID.xy) / " << mipLevelSize.x() << ".0;\n"
651             << "    vec4 color = texture(sampler2D(image, samp), texCoord);\n"
652             << "    imageStore(verifyImage, uv, color);\n"
653             << "}\n";
654     }
655     else if (m_testParameters.imageType == CombinedImageSampler)
656     {
657         src << "#version 450 core\n"
658             << "layout (local_size_x = 1, local_size_y = 1) in;\n"
659             << "layout (binding = 0) uniform sampler2D combinedSampler;\n"
660             << "layout (rgba8, set=0, binding=1) writeonly uniform highp image2D verifyImage;\n"
661             << "void main (void) {\n"
662             << "    ivec2 uv = ivec2(gl_GlobalInvocationID.xy);\n"
663             << "    vec2 texCoord = vec2(gl_GlobalInvocationID.xy) / " << mipLevelSize.x() << ".0;\n"
664             << "    vec4 color = texture(combinedSampler, texCoord);\n"
665             << "    imageStore(verifyImage, uv, color);\n"
666             << "}\n";
667     }
668 
669     sourceCollections.glslSources.add("comp") << glu::ComputeSource(src.str());
670 }
671 
createInstance(Context & context) const672 TestInstance *ComputeImage2DView3DImageTest::createInstance(Context &context) const
673 {
674     return new Image2DView3DImageInstance(context, m_testParameters);
675 }
676 
677 class FragmentImage2DView3DImageTest : public vkt::TestCase
678 {
679 public:
FragmentImage2DView3DImageTest(tcu::TestContext & testContext,const char * name,const TestParameters & testParameters)680     FragmentImage2DView3DImageTest(tcu::TestContext &testContext, const char *name,
681                                    const TestParameters &testParameters)
682         : vkt::TestCase(testContext, name)
683         , m_testParameters(testParameters)
684     {
685     }
~FragmentImage2DView3DImageTest(void)686     virtual ~FragmentImage2DView3DImageTest(void)
687     {
688     }
689 
690     virtual void initPrograms(SourceCollections &sourceCollections) const;
691     virtual void checkSupport(Context &context) const;
692     virtual TestInstance *createInstance(Context &context) const;
693 
694 private:
695     const TestParameters m_testParameters;
696 };
697 
initPrograms(SourceCollections & sourceCollections) const698 void FragmentImage2DView3DImageTest::initPrograms(SourceCollections &sourceCollections) const
699 {
700     std::stringstream vertShader;
701     vertShader << "#version 450 core\n"
702                << "layout(location = 0) in vec4 in_position;\n"
703                << "out gl_PerVertex {\n"
704                << "    vec4  gl_Position;\n"
705                << "    float gl_PointSize;\n"
706                << "};\n"
707                << "void main() {\n"
708                << "    gl_PointSize = 1.0;\n"
709                << "    gl_Position  = in_position;\n"
710                << "}\n";
711     sourceCollections.glslSources.add("vert") << glu::VertexSource(vertShader.str());
712 
713     tcu::IVec3 mipLevelSize = computeMipLevelSize(m_testParameters.imageSize, m_testParameters.mipLevel);
714     std::stringstream fragShader;
715     if (m_testParameters.imageType == StorageImage)
716     {
717         fragShader << "#version 450 core\n"
718                    << "layout(rgba8, set = 0, binding = 0) uniform image2D storageImage;\n"
719                    << "void main()\n"
720                    << "{\n"
721                    << "    ivec2 uv = ivec2(gl_FragCoord.xy);\n"
722                    << "    float c = float((uv.x + uv.y) & 1);\n"
723                    << "    vec4 color = vec4(c, c, c, 1.0);\n"
724                    << "    imageStore(storageImage, uv, color);\n"
725                    << "}\n";
726     }
727 
728     else if (m_testParameters.imageType == Sampler)
729     {
730         fragShader << "#version 450 core\n"
731                    << "layout (set = 0, binding = 0) uniform texture2D image;\n"
732                    << "layout (set = 0, binding = 1) uniform sampler samp;\n"
733                    << "layout (rgba8, set = 0, binding = 2) uniform image2D verifyImage;\n"
734                    << "void main (void) {\n"
735                    << "    ivec2 uv = ivec2(gl_FragCoord.xy);\n"
736                    << "    vec2 texCoord = gl_FragCoord.xy / " << mipLevelSize.x() << ".0;\n"
737                    << "    vec4 color = texture(sampler2D(image, samp), texCoord);\n"
738                    << "    imageStore(verifyImage, uv, color);\n"
739                    << "}\n";
740     }
741     else if (m_testParameters.imageType == CombinedImageSampler)
742     {
743         fragShader << "#version 450 core\n"
744                    << "layout (set = 0, binding = 0) uniform sampler2D combinedSampler;\n"
745                    << "layout (rgba8, set = 0, binding = 1) uniform image2D verifyImage;\n"
746                    << "void main (void) {\n"
747                    << "    ivec2 uv = ivec2(gl_FragCoord.xy);\n"
748                    << "    vec2 texCoord = gl_FragCoord.xy / " << mipLevelSize.x() << ".0;\n"
749                    << "    vec4 color = texture(combinedSampler, texCoord);\n"
750                    << "    imageStore(verifyImage, uv, color);\n"
751                    << "}\n";
752     }
753     sourceCollections.glslSources.add("frag") << glu::FragmentSource(fragShader.str());
754 }
755 
checkSupport(Context & context) const756 void FragmentImage2DView3DImageTest::checkSupport(Context &context) const
757 {
758     checkPipelineConstructionRequirements(context.getInstanceInterface(), context.getPhysicalDevice(),
759                                           m_testParameters.pipelineConstructionType);
760 
761     if (!context.isDeviceFunctionalitySupported("VK_EXT_image_2d_view_of_3d"))
762         TCU_THROW(NotSupportedError, "VK_EXT_image_2d_view_of_3d functionality not supported.");
763 
764     if (!context.getImage2DViewOf3DFeaturesEXT().image2DViewOf3D)
765         TCU_THROW(NotSupportedError, "image2DViewOf3D not supported.");
766 
767     if (m_testParameters.imageType != StorageImage && !context.getImage2DViewOf3DFeaturesEXT().sampler2DViewOf3D)
768         TCU_THROW(NotSupportedError, "texture2DViewOf3D not supported.");
769 
770     if (!context.getDeviceFeatures().fragmentStoresAndAtomics)
771         TCU_THROW(NotSupportedError, "fragmentStoresAndAtomics not supported");
772 }
773 
createInstance(Context & context) const774 TestInstance *FragmentImage2DView3DImageTest::createInstance(Context &context) const
775 {
776     return new Image2DView3DImageInstance(context, m_testParameters);
777 }
778 
779 } // namespace
780 
createImage2DViewOf3DTests(tcu::TestContext & testCtx,PipelineConstructionType pipelineConstructionType)781 tcu::TestCaseGroup *createImage2DViewOf3DTests(tcu::TestContext &testCtx,
782                                                PipelineConstructionType pipelineConstructionType)
783 {
784     de::MovePtr<tcu::TestCaseGroup> imageTests(new tcu::TestCaseGroup(testCtx, "image_2d_view_3d_image"));
785     de::MovePtr<tcu::TestCaseGroup> computeGroup(new tcu::TestCaseGroup(testCtx, "compute"));
786     de::MovePtr<tcu::TestCaseGroup> fragmentGroup(new tcu::TestCaseGroup(testCtx, "fragment"));
787 
788     const struct
789     {
790         const ImageAccessType imageType;
791         const std::string name;
792     } imageAccessTypes[]{
793         {StorageImage, "storage"}, {Sampler, "sampler"}, {CombinedImageSampler, "combined_image_sampler"}};
794 
795     const int32_t imageDimension = 64;
796     for (const auto &imageAccessType : imageAccessTypes)
797     {
798         de::MovePtr<tcu::TestCaseGroup> computeSubGroup(new tcu::TestCaseGroup(testCtx, imageAccessType.name.c_str()));
799         de::MovePtr<tcu::TestCaseGroup> fragmentSubGroup(new tcu::TestCaseGroup(testCtx, imageAccessType.name.c_str()));
800         for (uint32_t mipLevel = 0; mipLevel < 3; mipLevel += 2)
801         {
802             // Test the first and the last layer of the mip level.
803             std::vector<int32_t> layers = {0, computeMipLevelDimension(imageDimension, mipLevel) - 1};
804             for (const auto &layer : layers)
805             {
806                 TestParameters testParameters{
807                     tcu::IVec3(imageDimension), // IVec3                        imageSize
808                     mipLevel,                   // uint32_t                        mipLevel
809                     layer,                      // int32_t                        layerNdx
810                     imageAccessType.imageType,  // ImageAccessType                imageType
811                     Fragment,                   // TestType                        testType
812                     VK_FORMAT_R8G8B8A8_UNORM,   // VkFormat                        imageFormat
813                     pipelineConstructionType    // PipelineConstructionType        pipelineConstructionType
814                 };
815                 std::string testName = "mip" + std::to_string(mipLevel) + "_layer" + std::to_string(layer);
816                 fragmentSubGroup->addChild(
817                     new FragmentImage2DView3DImageTest(testCtx, testName.c_str(), testParameters));
818 
819                 if (pipelineConstructionType == PIPELINE_CONSTRUCTION_TYPE_MONOLITHIC)
820                 {
821                     testParameters.testType = Compute;
822                     computeSubGroup->addChild(
823                         new ComputeImage2DView3DImageTest(testCtx, testName.c_str(), testParameters));
824                 }
825             }
826         }
827         computeGroup->addChild(computeSubGroup.release());
828         fragmentGroup->addChild(fragmentSubGroup.release());
829     }
830 
831     imageTests->addChild(computeGroup.release());
832     imageTests->addChild(fragmentGroup.release());
833     return imageTests.release();
834 }
835 
836 } // namespace pipeline
837 } // namespace vkt
838