1 /*------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2016 The Khronos Group Inc.
6  * Copyright (c) 2016 The Android Open Source Project
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  *//*!
21  * \file
22  * \brief Multisampled image load/store Tests
23  *//*--------------------------------------------------------------------*/
24 
25 #include "vktImageMultisampleLoadStoreTests.hpp"
26 #include "vktTestCaseUtil.hpp"
27 #include "vktImageTestsUtil.hpp"
28 #include "vktImageLoadStoreUtil.hpp"
29 #include "vktImageTexture.hpp"
30 
31 #include "vkDefs.hpp"
32 #include "vkRef.hpp"
33 #include "vkRefUtil.hpp"
34 #include "vkPlatform.hpp"
35 #include "vkPrograms.hpp"
36 #include "vkMemUtil.hpp"
37 #include "vkBarrierUtil.hpp"
38 #include "vkBuilderUtil.hpp"
39 #include "vkQueryUtil.hpp"
40 #include "vkImageUtil.hpp"
41 #include "vkCmdUtil.hpp"
42 #include "vkObjUtil.hpp"
43 #include "vkBufferWithMemory.hpp"
44 
45 #include "deUniquePtr.hpp"
46 
47 #include "tcuTextureUtil.hpp"
48 #include "tcuTestLog.hpp"
49 
50 #include <string>
51 #include <vector>
52 
53 namespace vkt
54 {
55 namespace image
56 {
57 namespace
58 {
59 using namespace vk;
60 using de::MovePtr;
61 using de::UniquePtr;
62 using tcu::IVec3;
63 
64 static const VkFormat CHECKSUM_IMAGE_FORMAT = VK_FORMAT_R32_SINT;
65 
66 struct CaseDef
67 {
68     Texture texture;
69     VkFormat format;
70     VkSampleCountFlagBits numSamples;
71     bool singleLayerBind;
72 };
73 
74 //  Multisampled storage image test.
75 //
76 //  Pass 1: Write a slightly different color pattern per-sample to the whole image.
77 //  Pass 2: Read samples of the same image and check if color values are in the expected range.
78 //          Write back results as a checksum image and verify them on the host.
79 //  Each checksum image pixel should contain an integer equal to the number of samples.
80 
initPrograms(SourceCollections & programCollection,const CaseDef caseDef)81 void initPrograms(SourceCollections &programCollection, const CaseDef caseDef)
82 {
83     const int dimension = (caseDef.singleLayerBind ? caseDef.texture.layerDimension() : caseDef.texture.dimension());
84     const std::string texelCoordStr = (dimension == 1 ? "gx" :
85                                        dimension == 2 ? "ivec2(gx, gy)" :
86                                        dimension == 3 ? "ivec3(gx, gy, gz)" :
87                                                         "");
88 
89     const ImageType usedImageType =
90         (caseDef.singleLayerBind ? getImageTypeForSingleLayer(caseDef.texture.type()) : caseDef.texture.type());
91     const bool isAlphaOnly = isAlphaOnlyFormat(caseDef.format);
92     const std::string formatQualifierStr =
93         (isAlphaOnly ? "" : ", " + getShaderImageFormatQualifier(mapVkFormat(caseDef.format)));
94     const std::string msImageTypeStr =
95         getShaderImageType(mapVkFormat(caseDef.format), usedImageType, (caseDef.texture.numSamples() > 1));
96 
97     const std::string xMax             = de::toString(caseDef.texture.size().x() - 1);
98     const std::string yMax             = de::toString(caseDef.texture.size().y() - 1);
99     const std::string signednessPrefix = isUintFormat(caseDef.format) ? "u" : isIntFormat(caseDef.format) ? "i" : "";
100     const std::string gvec4Expr        = signednessPrefix + "vec4";
101     const int numColorComponents =
102         (isAlphaOnly ?
103              4 :
104              tcu::getNumUsedChannels(mapVkFormat(caseDef.format).order)); // Force 4 for A8_UNORM as per the spec.
105 
106     const float storeColorScale = computeStoreColorScale(caseDef.format, caseDef.texture.size());
107     const float storeColorBias  = computeStoreColorBias(caseDef.format);
108     DE_ASSERT(colorScaleAndBiasAreValid(caseDef.format, storeColorScale, storeColorBias));
109 
110     const std::string colorScaleExpr = (storeColorScale == 1.0f ? "" : "*" + de::toString(storeColorScale)) +
111                                        (storeColorBias == 0.0f ? "" : " + float(" + de::toString(storeColorBias) + ")");
112 
113     const std::string red =
114         "gx^gy^gz^(sampleNdx >> 5)^(sampleNdx & 31)"; // we "split" sampleNdx to keep this value in [0, 31] range for numSamples = 64 case
115     const std::string green = "(" + xMax + "-gx)^gy^gz";
116     const std::string blue  = "gx^(" + yMax + "-gy)^gz";
117     const std::string alpha = "(" + xMax + "-gx)^(" + yMax + "-gy)^gz";
118     const std::string colorExpr =
119         gvec4Expr + "(" + ((isAlphaOnly ? alpha : red) + ", ") // For A8_UNORM we switch the alpha and red values.
120         + (numColorComponents > 1 ? green + ", " : "0, ") + (numColorComponents > 2 ? blue + ", " : "0, ") +
121         (numColorComponents > 3 ? (isAlphaOnly ? red : alpha) : "1") + ")" + colorScaleExpr;
122 
123     const std::string expectedColorExpr =
124         gvec4Expr + "(" + ((isAlphaOnly ? "0" : red) + ", ") // A8_UNORM should result in RGB (0, 0, 0).
125         + ((numColorComponents > 1 && !isAlphaOnly) ? green + ", " : "0, ") +
126         ((numColorComponents > 2 && !isAlphaOnly) ? blue + ", " : "0, ") +
127         ((numColorComponents > 3) ? (isAlphaOnly ? red : alpha) : "1") + ")" + colorScaleExpr;
128 
129     // Store shader
130     {
131         std::ostringstream src;
132         src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
133             << (isAlphaOnly ? "#extension GL_EXT_shader_image_load_formatted : require\n" : "") << "\n"
134             << "layout(local_size_x = 1) in;\n"
135             << "layout(set = 0, binding = 1" << formatQualifierStr << ") writeonly uniform " << msImageTypeStr
136             << " u_msImage;\n";
137 
138         if (caseDef.singleLayerBind)
139             src << "layout(set = 0, binding = 0) readonly uniform Constants {\n"
140                 << "    int u_layerNdx;\n"
141                 << "};\n";
142 
143         src << "\n"
144             << "void main (void)\n"
145             << "{\n"
146             << "    int gx = int(gl_GlobalInvocationID.x);\n"
147             << "    int gy = int(gl_GlobalInvocationID.y);\n"
148             << "    int gz = " << (caseDef.singleLayerBind ? "u_layerNdx" : "int(gl_GlobalInvocationID.z)") << ";\n"
149             << "\n"
150             << "    for (int sampleNdx = 0; sampleNdx < " << caseDef.texture.numSamples() << "; ++sampleNdx) {\n"
151             << "        imageStore(u_msImage, " << texelCoordStr << ", sampleNdx, " << colorExpr << ");\n"
152             << "    }\n"
153             << "}\n";
154 
155         programCollection.glslSources.add("comp_store") << glu::ComputeSource(src.str());
156     }
157 
158     // Load shader
159     {
160         const tcu::TextureFormat checksumFormat = mapVkFormat(CHECKSUM_IMAGE_FORMAT);
161         const std::string checksumImageTypeStr  = getShaderImageType(checksumFormat, usedImageType);
162         const bool useExactCompare              = isIntegerFormat(caseDef.format);
163 
164         std::ostringstream src;
165         src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
166             << (isAlphaOnly ? "#extension GL_EXT_shader_image_load_formatted : require\n" : "") << "\n"
167             << "layout(local_size_x = 1) in;\n"
168             << "layout(set = 0, binding = 1" << formatQualifierStr << ") readonly  uniform " << msImageTypeStr
169             << " u_msImage;\n"
170             << "layout(set = 0, binding = 2, " << getShaderImageFormatQualifier(checksumFormat)
171             << ") writeonly uniform " << checksumImageTypeStr << " u_checksumImage;\n";
172 
173         if (caseDef.singleLayerBind)
174             src << "layout(set = 0, binding = 0) readonly uniform Constants {\n"
175                 << "    int u_layerNdx;\n"
176                 << "};\n";
177 
178         src << "\n"
179             << "void main (void)\n"
180             << "{\n"
181             << "    int gx = int(gl_GlobalInvocationID.x);\n"
182             << "    int gy = int(gl_GlobalInvocationID.y);\n"
183             << "    int gz = " << (caseDef.singleLayerBind ? "u_layerNdx" : "int(gl_GlobalInvocationID.z)") << ";\n"
184             << "\n"
185             << "    int checksum = 0;\n"
186             << "    for (int sampleNdx = 0; sampleNdx < " << caseDef.texture.numSamples() << "; ++sampleNdx) {\n"
187             << "        " << gvec4Expr << " color = imageLoad(u_msImage, " << texelCoordStr << ", sampleNdx);\n";
188 
189         if (useExactCompare)
190             src << "        if (color == " << expectedColorExpr << ")\n"
191                 << "            ++checksum;\n";
192         else
193             src << "        " << gvec4Expr << " diff  = abs(abs(color) - abs(" << expectedColorExpr << "));\n"
194                 << "        if (all(lessThan(diff, " << gvec4Expr << "(0.02))))\n"
195                 << "            ++checksum;\n";
196 
197         src << "    }\n"
198             << "\n"
199             << "    imageStore(u_checksumImage, " << texelCoordStr << ", ivec4(checksum));\n"
200             << "}\n";
201 
202         programCollection.glslSources.add("comp_load") << glu::ComputeSource(src.str());
203     }
204 }
205 
checkSupport(Context & context,const CaseDef caseDef)206 void checkSupport(Context &context, const CaseDef caseDef)
207 {
208     context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_SHADER_STORAGE_IMAGE_MULTISAMPLE);
209 
210 #ifndef CTS_USES_VULKANSC
211     if (caseDef.format == VK_FORMAT_A8_UNORM_KHR)
212         context.requireDeviceFunctionality("VK_KHR_maintenance5");
213 #endif // CTS_USES_VULKANSC
214 
215     VkImageFormatProperties imageFormatProperties;
216     const auto &vki                  = context.getInstanceInterface();
217     const auto physicalDevice        = context.getPhysicalDevice();
218     const VkResult imageFormatResult = vki.getPhysicalDeviceImageFormatProperties(
219         physicalDevice, caseDef.format, VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_STORAGE_BIT,
220         (VkImageCreateFlags)0, &imageFormatProperties);
221 
222     if (imageFormatResult == VK_ERROR_FORMAT_NOT_SUPPORTED)
223         TCU_THROW(NotSupportedError, "Format is not supported");
224 
225     if ((imageFormatProperties.sampleCounts & caseDef.numSamples) != caseDef.numSamples)
226         TCU_THROW(NotSupportedError, "Requested sample count is not supported");
227 
228 #ifndef CTS_USES_VULKANSC
229     if (caseDef.format == VK_FORMAT_A8_UNORM_KHR)
230     {
231         const auto formatProperties = context.getFormatProperties(caseDef.format);
232 
233         if ((formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_2_STORAGE_READ_WITHOUT_FORMAT_BIT) == 0u)
234             TCU_THROW(NotSupportedError, "Format does not support storage reads without format");
235 
236         if ((formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_2_STORAGE_WRITE_WITHOUT_FORMAT_BIT) == 0u)
237             TCU_THROW(NotSupportedError, "Format does not support storage writes without format");
238     }
239 #endif // CTS_USES_VULKANSC
240 }
241 
242 //! Helper function to deal with per-layer resources.
insertImageViews(const DeviceInterface & vk,const VkDevice device,const CaseDef & caseDef,const VkFormat format,const VkImage image,std::vector<SharedVkImageView> * const pOutImageViews)243 void insertImageViews(const DeviceInterface &vk, const VkDevice device, const CaseDef &caseDef, const VkFormat format,
244                       const VkImage image, std::vector<SharedVkImageView> *const pOutImageViews)
245 {
246     if (caseDef.singleLayerBind)
247     {
248         pOutImageViews->clear();
249         pOutImageViews->resize(caseDef.texture.numLayers());
250         for (int layerNdx = 0; layerNdx < caseDef.texture.numLayers(); ++layerNdx)
251         {
252             (*pOutImageViews)[layerNdx] = makeVkSharedPtr(
253                 makeImageView(vk, device, image, mapImageViewType(getImageTypeForSingleLayer(caseDef.texture.type())),
254                               format, makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, layerNdx, 1u)));
255         }
256     }
257     else // bind all layers at once
258     {
259         pOutImageViews->clear();
260         pOutImageViews->resize(1);
261         (*pOutImageViews)[0] = makeVkSharedPtr(makeImageView(
262             vk, device, image, mapImageViewType(caseDef.texture.type()), format,
263             makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, caseDef.texture.numLayers())));
264     }
265 }
266 
267 //! Helper function to deal with per-layer resources.
insertDescriptorSets(const DeviceInterface & vk,const VkDevice device,const CaseDef & caseDef,const VkDescriptorPool descriptorPool,const VkDescriptorSetLayout descriptorSetLayout,std::vector<SharedVkDescriptorSet> * const pOutDescriptorSets)268 void insertDescriptorSets(const DeviceInterface &vk, const VkDevice device, const CaseDef &caseDef,
269                           const VkDescriptorPool descriptorPool, const VkDescriptorSetLayout descriptorSetLayout,
270                           std::vector<SharedVkDescriptorSet> *const pOutDescriptorSets)
271 {
272     if (caseDef.singleLayerBind)
273     {
274         pOutDescriptorSets->clear();
275         pOutDescriptorSets->resize(caseDef.texture.numLayers());
276         for (int layerNdx = 0; layerNdx < caseDef.texture.numLayers(); ++layerNdx)
277             (*pOutDescriptorSets)[layerNdx] =
278                 makeVkSharedPtr(makeDescriptorSet(vk, device, descriptorPool, descriptorSetLayout));
279     }
280     else // bind all layers at once
281     {
282         pOutDescriptorSets->clear();
283         pOutDescriptorSets->resize(1);
284         (*pOutDescriptorSets)[0] = makeVkSharedPtr(makeDescriptorSet(vk, device, descriptorPool, descriptorSetLayout));
285     }
286 }
287 
test(Context & context,const CaseDef caseDef)288 tcu::TestStatus test(Context &context, const CaseDef caseDef)
289 {
290     const InstanceInterface &vki      = context.getInstanceInterface();
291     const VkPhysicalDevice physDevice = context.getPhysicalDevice();
292     const DeviceInterface &vk         = context.getDeviceInterface();
293     const VkDevice device             = context.getDevice();
294     const VkQueue queue               = context.getUniversalQueue();
295     const uint32_t queueFamilyIndex   = context.getUniversalQueueFamilyIndex();
296     Allocator &allocator              = context.getDefaultAllocator();
297 
298     // Images
299 
300     const UniquePtr<Image> msImage(new Image(
301         vk, device, allocator, makeImageCreateInfo(caseDef.texture, caseDef.format, VK_IMAGE_USAGE_STORAGE_BIT, 0u),
302         MemoryRequirement::Any));
303 
304     const UniquePtr<Image> checksumImage(
305         new Image(vk, device, allocator,
306                   makeImageCreateInfo(Texture(caseDef.texture, 1), CHECKSUM_IMAGE_FORMAT,
307                                       VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, 0u),
308                   MemoryRequirement::Any));
309 
310     // Buffer used to pass constants to the shader.
311 
312     const int numLayers                         = caseDef.texture.numLayers();
313     const VkDeviceSize bufferChunkSize          = getOptimalUniformBufferChunkSize(vki, physDevice, sizeof(int32_t));
314     const VkDeviceSize constantsBufferSizeBytes = numLayers * bufferChunkSize;
315     UniquePtr<BufferWithMemory> constantsBuffer(new BufferWithMemory(
316         vk, device, allocator, makeBufferCreateInfo(constantsBufferSizeBytes, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT),
317         MemoryRequirement::HostVisible));
318 
319     {
320         const Allocation &alloc = constantsBuffer->getAllocation();
321         uint8_t *const basePtr  = static_cast<uint8_t *>(alloc.getHostPtr());
322 
323         deMemset(alloc.getHostPtr(), 0, static_cast<size_t>(constantsBufferSizeBytes));
324 
325         for (int layerNdx = 0; layerNdx < numLayers; ++layerNdx)
326         {
327             int32_t *const valuePtr = reinterpret_cast<int32_t *>(basePtr + layerNdx * bufferChunkSize);
328             *valuePtr               = layerNdx;
329         }
330 
331         flushAlloc(vk, device, alloc);
332     }
333 
334     const VkDeviceSize resultBufferSizeBytes = getImageSizeBytes(caseDef.texture.size(), CHECKSUM_IMAGE_FORMAT);
335     UniquePtr<BufferWithMemory> resultBuffer(new BufferWithMemory(
336         vk, device, allocator, makeBufferCreateInfo(resultBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT),
337         MemoryRequirement::HostVisible));
338 
339     {
340         const Allocation &alloc = resultBuffer->getAllocation();
341         deMemset(alloc.getHostPtr(), 0, static_cast<size_t>(resultBufferSizeBytes));
342         flushAlloc(vk, device, alloc);
343     }
344 
345     // Descriptors
346 
347     Unique<VkDescriptorSetLayout> descriptorSetLayout(
348         DescriptorSetLayoutBuilder()
349             .addSingleBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_COMPUTE_BIT)
350             .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT)
351             .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT)
352             .build(vk, device));
353 
354     Unique<VkDescriptorPool> descriptorPool(
355         DescriptorPoolBuilder()
356             .addType(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, numLayers)
357             .addType(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, numLayers)
358             .addType(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, numLayers)
359             .build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, numLayers));
360 
361     std::vector<SharedVkDescriptorSet> allDescriptorSets;
362     std::vector<SharedVkImageView> allMultisampledImageViews;
363     std::vector<SharedVkImageView> allChecksumImageViews;
364 
365     insertDescriptorSets(vk, device, caseDef, *descriptorPool, *descriptorSetLayout, &allDescriptorSets);
366     insertImageViews(vk, device, caseDef, caseDef.format, **msImage, &allMultisampledImageViews);
367     insertImageViews(vk, device, caseDef, CHECKSUM_IMAGE_FORMAT, **checksumImage, &allChecksumImageViews);
368 
369     // Prepare commands
370 
371     const Unique<VkPipelineLayout> pipelineLayout(makePipelineLayout(vk, device, *descriptorSetLayout));
372     const Unique<VkCommandPool> cmdPool(
373         createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex));
374     const Unique<VkCommandBuffer> cmdBuffer(
375         allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
376 
377     const tcu::IVec3 workSize = (caseDef.singleLayerBind ? caseDef.texture.layerSize() : caseDef.texture.size());
378     const int loopNumLayers   = (caseDef.singleLayerBind ? numLayers : 1);
379     const VkImageSubresourceRange subresourceAllLayers =
380         makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, caseDef.texture.numLayers());
381 
382     // Pass 1: Write MS image
383     {
384         const Unique<VkShaderModule> shaderModule(
385             createShaderModule(vk, device, context.getBinaryCollection().get("comp_store"), 0));
386         const Unique<VkPipeline> pipeline(makeComputePipeline(vk, device, *pipelineLayout, *shaderModule));
387 
388         beginCommandBuffer(vk, *cmdBuffer);
389         vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
390 
391         {
392             const VkImageMemoryBarrier barriers[] = {
393                 makeImageMemoryBarrier((VkAccessFlags)0, VK_ACCESS_SHADER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED,
394                                        VK_IMAGE_LAYOUT_GENERAL, **msImage, subresourceAllLayers),
395                 makeImageMemoryBarrier((VkAccessFlags)0, VK_ACCESS_SHADER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED,
396                                        VK_IMAGE_LAYOUT_GENERAL, **checksumImage, subresourceAllLayers),
397             };
398 
399             vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
400                                   (VkDependencyFlags)0, 0u, DE_NULL, 0u, DE_NULL, DE_LENGTH_OF_ARRAY(barriers),
401                                   barriers);
402         }
403 
404         for (int layerNdx = 0; layerNdx < loopNumLayers; ++layerNdx)
405         {
406             const VkDescriptorSet descriptorSet = **allDescriptorSets[layerNdx];
407             const VkDescriptorImageInfo descriptorMultiImageInfo =
408                 makeDescriptorImageInfo(DE_NULL, **allMultisampledImageViews[layerNdx], VK_IMAGE_LAYOUT_GENERAL);
409             const VkDescriptorBufferInfo descriptorConstantsBufferInfo =
410                 makeDescriptorBufferInfo(constantsBuffer->get(), layerNdx * bufferChunkSize, bufferChunkSize);
411 
412             DescriptorSetUpdateBuilder()
413                 .writeSingle(descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u),
414                              VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &descriptorConstantsBufferInfo)
415                 .writeSingle(descriptorSet, DescriptorSetUpdateBuilder::Location::binding(1u),
416                              VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &descriptorMultiImageInfo)
417                 .update(vk, device);
418 
419             vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0u, 1u,
420                                      &descriptorSet, 0u, DE_NULL);
421             vk.cmdDispatch(*cmdBuffer, workSize.x(), workSize.y(), workSize.z());
422         }
423 
424         endCommandBuffer(vk, *cmdBuffer);
425         submitCommandsAndWait(vk, device, queue, *cmdBuffer);
426         context.resetCommandPoolForVKSC(device, *cmdPool);
427     }
428 
429     // Pass 2: "Resolve" MS image in compute shader
430     {
431         const Unique<VkShaderModule> shaderModule(
432             createShaderModule(vk, device, context.getBinaryCollection().get("comp_load"), 0));
433         const Unique<VkPipeline> pipeline(makeComputePipeline(vk, device, *pipelineLayout, *shaderModule));
434 
435         beginCommandBuffer(vk, *cmdBuffer);
436         vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
437 
438         {
439             const VkImageMemoryBarrier barriers[] = {
440                 makeImageMemoryBarrier(VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_LAYOUT_GENERAL,
441                                        VK_IMAGE_LAYOUT_GENERAL, **msImage, subresourceAllLayers),
442             };
443 
444             vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
445                                   VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, (VkDependencyFlags)0, 0u, DE_NULL, 0u, DE_NULL,
446                                   DE_LENGTH_OF_ARRAY(barriers), barriers);
447         }
448 
449         for (int layerNdx = 0; layerNdx < loopNumLayers; ++layerNdx)
450         {
451             const VkDescriptorSet descriptorSet = **allDescriptorSets[layerNdx];
452             const VkDescriptorImageInfo descriptorMultiImageInfo =
453                 makeDescriptorImageInfo(DE_NULL, **allMultisampledImageViews[layerNdx], VK_IMAGE_LAYOUT_GENERAL);
454             const VkDescriptorImageInfo descriptorChecksumImageInfo =
455                 makeDescriptorImageInfo(DE_NULL, **allChecksumImageViews[layerNdx], VK_IMAGE_LAYOUT_GENERAL);
456             const VkDescriptorBufferInfo descriptorConstantsBufferInfo =
457                 makeDescriptorBufferInfo(constantsBuffer->get(), layerNdx * bufferChunkSize, bufferChunkSize);
458 
459             DescriptorSetUpdateBuilder()
460                 .writeSingle(descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u),
461                              VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &descriptorConstantsBufferInfo)
462                 .writeSingle(descriptorSet, DescriptorSetUpdateBuilder::Location::binding(1u),
463                              VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &descriptorMultiImageInfo)
464                 .writeSingle(descriptorSet, DescriptorSetUpdateBuilder::Location::binding(2u),
465                              VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &descriptorChecksumImageInfo)
466                 .update(vk, device);
467 
468             vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0u, 1u,
469                                      &descriptorSet, 0u, DE_NULL);
470             vk.cmdDispatch(*cmdBuffer, workSize.x(), workSize.y(), workSize.z());
471         }
472 
473         endCommandBuffer(vk, *cmdBuffer);
474         submitCommandsAndWait(vk, device, queue, *cmdBuffer);
475         context.resetCommandPoolForVKSC(device, *cmdPool);
476     }
477 
478     // Retrieve result
479     {
480         beginCommandBuffer(vk, *cmdBuffer);
481 
482         {
483             const VkImageMemoryBarrier barriers[] = {
484                 makeImageMemoryBarrier(VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_LAYOUT_GENERAL,
485                                        VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, **checksumImage, subresourceAllLayers),
486             };
487             vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
488                                   (VkDependencyFlags)0, 0u, DE_NULL, 0u, DE_NULL, DE_LENGTH_OF_ARRAY(barriers),
489                                   barriers);
490         }
491         {
492             const VkBufferImageCopy copyRegion =
493                 makeBufferImageCopy(makeExtent3D(caseDef.texture.layerSize()), caseDef.texture.numLayers());
494             vk.cmdCopyImageToBuffer(*cmdBuffer, **checksumImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, **resultBuffer,
495                                     1u, &copyRegion);
496         }
497         {
498             const VkBufferMemoryBarrier barriers[] = {
499                 makeBufferMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, **resultBuffer, 0ull,
500                                         resultBufferSizeBytes),
501             };
502             vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT,
503                                   (VkDependencyFlags)0, 0u, DE_NULL, DE_LENGTH_OF_ARRAY(barriers), barriers, 0u,
504                                   DE_NULL);
505         }
506 
507         endCommandBuffer(vk, *cmdBuffer);
508         submitCommandsAndWait(vk, device, queue, *cmdBuffer);
509     }
510 
511     // Verify
512     {
513         const Allocation &alloc = resultBuffer->getAllocation();
514         invalidateAlloc(vk, device, alloc);
515 
516         const IVec3 imageSize          = caseDef.texture.size();
517         const int32_t *pDataPtr        = static_cast<int32_t *>(alloc.getHostPtr());
518         const int32_t expectedChecksum = caseDef.texture.numSamples();
519 
520         for (int layer = 0; layer < imageSize.z(); ++layer)
521             for (int y = 0; y < imageSize.y(); ++y)
522                 for (int x = 0; x < imageSize.x(); ++x)
523                 {
524                     if (*pDataPtr != expectedChecksum)
525                     {
526                         context.getTestContext().getLog()
527                             << tcu::TestLog::Message << "Some sample colors were incorrect at (x, y, layer) = (" << x
528                             << ", " << y << ", " << layer << ")" << tcu::TestLog::EndMessage << tcu::TestLog::Message
529                             << "Checksum value is " << *pDataPtr << " but expected " << expectedChecksum
530                             << tcu::TestLog::EndMessage;
531 
532                         return tcu::TestStatus::fail("Some sample colors were incorrect");
533                     }
534                     ++pDataPtr;
535                 }
536 
537         return tcu::TestStatus::pass("OK");
538     }
539 }
540 
541 } // namespace
542 
createImageMultisampleLoadStoreTests(tcu::TestContext & testCtx)543 tcu::TestCaseGroup *createImageMultisampleLoadStoreTests(tcu::TestContext &testCtx)
544 {
545     const Texture textures[] = {
546         // \note Shader code is tweaked to work with image size of 32, take a look if this needs to be modified.
547         Texture(IMAGE_TYPE_2D, tcu::IVec3(32, 32, 1), 1),
548         Texture(IMAGE_TYPE_2D_ARRAY, tcu::IVec3(32, 32, 1), 4),
549     };
550 
551     static const VkFormat formats[] = {
552         VK_FORMAT_R32G32B32A32_SFLOAT, VK_FORMAT_R16G16B16A16_SFLOAT, VK_FORMAT_R32_SFLOAT,
553 
554         VK_FORMAT_R32G32B32A32_UINT,   VK_FORMAT_R16G16B16A16_UINT,   VK_FORMAT_R8G8B8A8_UINT, VK_FORMAT_R32_UINT,
555 
556         VK_FORMAT_R32G32B32A32_SINT,   VK_FORMAT_R16G16B16A16_SINT,   VK_FORMAT_R8G8B8A8_SINT, VK_FORMAT_R32_SINT,
557 
558         VK_FORMAT_R8G8B8A8_UNORM,
559 
560         VK_FORMAT_R8G8B8A8_SNORM,
561 
562 #ifndef CTS_USES_VULKANSC
563         VK_FORMAT_A8_UNORM_KHR,
564 #endif // CTS_USES_VULKANSC
565     };
566 
567     static const VkSampleCountFlagBits samples[] = {
568         VK_SAMPLE_COUNT_2_BIT,  VK_SAMPLE_COUNT_4_BIT,  VK_SAMPLE_COUNT_8_BIT,
569         VK_SAMPLE_COUNT_16_BIT, VK_SAMPLE_COUNT_32_BIT, VK_SAMPLE_COUNT_64_BIT,
570     };
571 
572     MovePtr<tcu::TestCaseGroup> testGroup(new tcu::TestCaseGroup(testCtx, "load_store_multisample"));
573 
574     for (int baseTextureNdx = 0; baseTextureNdx < DE_LENGTH_OF_ARRAY(textures); ++baseTextureNdx)
575     {
576         const Texture &baseTexture = textures[baseTextureNdx];
577         MovePtr<tcu::TestCaseGroup> imageViewGroup(
578             new tcu::TestCaseGroup(testCtx, getImageTypeName(baseTexture.type()).c_str()));
579         const int numLayerBindModes = (baseTexture.numLayers() == 1 ? 1 : 2);
580 
581         for (int formatNdx = 0; formatNdx < DE_LENGTH_OF_ARRAY(formats); ++formatNdx)
582             for (int layerBindMode = 0; layerBindMode < numLayerBindModes; ++layerBindMode)
583             {
584                 const bool singleLayerBind = (layerBindMode != 0);
585                 const std::string formatGroupName =
586                     getFormatShortString(formats[formatNdx]) + (singleLayerBind ? "_single_layer" : "");
587                 MovePtr<tcu::TestCaseGroup> formatGroup(new tcu::TestCaseGroup(testCtx, formatGroupName.c_str()));
588 
589                 for (int samplesNdx = 0; samplesNdx < DE_LENGTH_OF_ARRAY(samples); ++samplesNdx)
590                 {
591                     const std::string samplesCaseName = "samples_" + de::toString(samples[samplesNdx]);
592 
593                     const CaseDef caseDef = {
594                         Texture(baseTexture, samples[samplesNdx]),
595                         formats[formatNdx],
596                         samples[samplesNdx],
597                         singleLayerBind,
598                     };
599 
600                     addFunctionCaseWithPrograms(formatGroup.get(), samplesCaseName, checkSupport, initPrograms, test,
601                                                 caseDef);
602                 }
603                 imageViewGroup->addChild(formatGroup.release());
604             }
605         testGroup->addChild(imageViewGroup.release());
606     }
607 
608     return testGroup.release();
609 }
610 
611 } // namespace image
612 } // namespace vkt
613