1 /*------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2018 The Khronos Group Inc.
6  * Copyright (c) 2018 Google Inc.
7  * Copyright (c) 2017 Samsung Electronics Co., Ltd.
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  *
21  *//*!
22  * \file
23  * \brief Protected memory workgroup storage tests
24  *//*--------------------------------------------------------------------*/
25 
26 #include "vktProtectedMemWorkgroupStorageTests.hpp"
27 
28 #include "vktProtectedMemContext.hpp"
29 #include "vktProtectedMemUtils.hpp"
30 #include "vktProtectedMemImageValidator.hpp"
31 #include "vktTestCase.hpp"
32 #include "vktTestGroupUtil.hpp"
33 
34 #include "vkPrograms.hpp"
35 #include "vkTypeUtil.hpp"
36 #include "vkBuilderUtil.hpp"
37 #include "vkImageUtil.hpp"
38 #include "vkCmdUtil.hpp"
39 #include "vkObjUtil.hpp"
40 
41 #include "tcuTestLog.hpp"
42 #include "tcuVector.hpp"
43 #include "tcuTextureUtil.hpp"
44 #include "tcuStringTemplate.hpp"
45 
46 #include "gluTextureTestUtil.hpp"
47 
48 #include "deRandom.hpp"
49 
50 namespace vkt
51 {
52 namespace ProtectedMem
53 {
54 
55 namespace
56 {
57 
58 struct Params
59 {
60     uint32_t sharedMemorySize;
61     uint32_t imageWidth;
62     uint32_t imageHeight;
63 
Paramsvkt::ProtectedMem::__anon67d25a1c0111::Params64     Params(uint32_t sharedMemorySize_) : sharedMemorySize(sharedMemorySize_)
65     {
66         // Find suitable image dimensions based on shared memory size
67         imageWidth         = 1;
68         imageHeight        = 1;
69         bool increaseWidth = true;
70         while (imageWidth * imageHeight < sharedMemorySize)
71         {
72             if (increaseWidth)
73                 imageWidth *= 2;
74             else
75                 imageHeight *= 2;
76 
77             increaseWidth = !increaseWidth;
78         }
79     }
80 };
81 
getSeedValue(const Params & params)82 uint32_t getSeedValue(const Params &params)
83 {
84     return deInt32Hash(params.sharedMemorySize);
85 }
86 
87 class WorkgroupStorageTestInstance : public ProtectedTestInstance
88 {
89 public:
90     WorkgroupStorageTestInstance(Context &ctx, const ImageValidator &validator, const Params &params);
91     virtual tcu::TestStatus iterate(void);
92 
93 private:
94     de::MovePtr<tcu::Texture2D> createTestTexture2D(void);
95     tcu::TestStatus validateResult(vk::VkImage image, vk::VkImageLayout imageLayout, const tcu::Texture2D &texture2D,
96                                    const tcu::Sampler &refSampler);
97     void calculateRef(tcu::Texture2D &texture2D);
98 
99     const ImageValidator &m_validator;
100     const Params &m_params;
101 };
102 
103 class WorkgroupStorageTestCase : public TestCase
104 {
105 public:
WorkgroupStorageTestCase(tcu::TestContext & testCtx,const std::string & name,const Params & params)106     WorkgroupStorageTestCase(tcu::TestContext &testCtx, const std::string &name, const Params &params)
107         : TestCase(testCtx, name)
108         , m_validator(vk::VK_FORMAT_R8G8B8A8_UNORM)
109         , m_params(params)
110     {
111     }
112 
~WorkgroupStorageTestCase(void)113     virtual ~WorkgroupStorageTestCase(void)
114     {
115     }
createInstance(Context & ctx) const116     virtual TestInstance *createInstance(Context &ctx) const
117     {
118         return new WorkgroupStorageTestInstance(ctx, m_validator, m_params);
119     }
120     virtual void initPrograms(vk::SourceCollections &programCollection) const;
checkSupport(Context & context) const121     virtual void checkSupport(Context &context) const
122     {
123         checkProtectedQueueSupport(context);
124     }
125 
126 private:
127     ImageValidator m_validator;
128     Params m_params;
129 };
130 
initPrograms(vk::SourceCollections & programCollection) const131 void WorkgroupStorageTestCase::initPrograms(vk::SourceCollections &programCollection) const
132 {
133     m_validator.initPrograms(programCollection);
134 
135     // Fill shared data array with source image data. Output result color with results from
136     // shared memory written by another invocation.
137     std::string comp = std::string() +
138                        "#version 450\n"
139                        "layout(local_size_x = " +
140                        de::toString(m_params.imageWidth) + ", local_size_y = " + de::toString(m_params.imageHeight) +
141                        ", local_size_z = 1) in;\n"
142                        "layout(set = 0, binding = 0, rgba8) writeonly uniform highp image2D u_resultImage;\n"
143                        "layout(set = 0, binding = 1, rgba8) readonly uniform highp image2D u_srcImage;\n"
144                        "shared vec4 sharedData[" +
145                        de::toString(m_params.sharedMemorySize) +
146                        "];\n"
147                        "\n"
148                        "void main() {\n"
149                        "    int gx = int(gl_GlobalInvocationID.x);\n"
150                        "    int gy = int(gl_GlobalInvocationID.y);\n"
151                        "    int s = " +
152                        de::toString(m_params.sharedMemorySize) +
153                        ";\n"
154                        "    int idx0 = gy * " +
155                        de::toString(m_params.imageWidth) +
156                        " + gx;\n"
157                        "    int idx1 = (idx0 + 1) % s;\n"
158                        "    vec4 color = imageLoad(u_srcImage, ivec2(gx, gy));\n"
159                        "    if (idx0 < s)\n"
160                        "    {\n"
161                        "        sharedData[idx0] = color;\n"
162                        "    }\n"
163                        "    barrier();\n"
164                        "    vec4 outColor = sharedData[idx1];\n"
165                        "    imageStore(u_resultImage, ivec2(gx, gy), outColor);\n"
166                        "}\n";
167 
168     programCollection.glslSources.add("comp") << glu::ComputeSource(comp);
169 }
170 
WorkgroupStorageTestInstance(Context & ctx,const ImageValidator & validator,const Params & params)171 WorkgroupStorageTestInstance::WorkgroupStorageTestInstance(Context &ctx, const ImageValidator &validator,
172                                                            const Params &params)
173     : ProtectedTestInstance(ctx)
174     , m_validator(validator)
175     , m_params(params)
176 {
177 }
178 
createTestTexture2D(void)179 de::MovePtr<tcu::Texture2D> WorkgroupStorageTestInstance::createTestTexture2D(void)
180 {
181     const tcu::TextureFormat texFmt      = mapVkFormat(vk::VK_FORMAT_R8G8B8A8_UNORM);
182     const tcu::TextureFormatInfo fmtInfo = tcu::getTextureFormatInfo(texFmt);
183     de::MovePtr<tcu::Texture2D> texture2D(new tcu::Texture2D(texFmt, m_params.imageWidth, m_params.imageHeight));
184 
185     texture2D->allocLevel(0);
186 
187     const tcu::PixelBufferAccess &level = texture2D->getLevel(0);
188 
189     fillWithRandomColorTiles(level, fmtInfo.valueMin, fmtInfo.valueMax, getSeedValue(m_params));
190 
191     return texture2D;
192 }
193 
iterate(void)194 tcu::TestStatus WorkgroupStorageTestInstance::iterate(void)
195 {
196     ProtectedContext &ctx(m_protectedContext);
197     const vk::DeviceInterface &vk   = ctx.getDeviceInterface();
198     const vk::VkDevice device       = ctx.getDevice();
199     const vk::VkQueue queue         = ctx.getQueue();
200     const uint32_t queueFamilyIndex = ctx.getQueueFamilyIndex();
201     const vk::VkPhysicalDeviceProperties properties =
202         vk::getPhysicalDeviceProperties(ctx.getInstanceDriver(), ctx.getPhysicalDevice());
203 
204     vk::Unique<vk::VkCommandPool> cmdPool(makeCommandPool(vk, device, PROTECTION_ENABLED, queueFamilyIndex));
205 
206     de::MovePtr<tcu::Texture2D> texture2D = createTestTexture2D();
207     const tcu::Sampler refSampler         = tcu::Sampler(
208         tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::NEAREST,
209         tcu::Sampler::NEAREST, 00.0f /* LOD threshold */, true /* normalized coords */, tcu::Sampler::COMPAREMODE_NONE,
210         0 /* cmp channel */, tcu::Vec4(0.0f) /* border color */, true /* seamless cube map */);
211 
212     vk::Unique<vk::VkShaderModule> computeShader(
213         vk::createShaderModule(vk, device, ctx.getBinaryCollection().get("comp"), 0));
214 
215     de::MovePtr<vk::ImageWithMemory> imageSrc;
216     de::MovePtr<vk::ImageWithMemory> imageDst;
217     vk::Move<vk::VkSampler> sampler;
218     vk::Move<vk::VkImageView> imageViewSrc;
219     vk::Move<vk::VkImageView> imageViewDst;
220 
221     vk::Move<vk::VkDescriptorSetLayout> descriptorSetLayout;
222     vk::Move<vk::VkDescriptorPool> descriptorPool;
223     vk::Move<vk::VkDescriptorSet> descriptorSet;
224 
225     // Check there is enough shared memory supported
226     if (properties.limits.maxComputeSharedMemorySize < m_params.sharedMemorySize * 4 * 4)
227         throw tcu::NotSupportedError("Not enough shared memory supported.");
228 
229     // Check the number of invocations supported
230     if (properties.limits.maxComputeWorkGroupInvocations < m_params.imageWidth * m_params.imageHeight)
231         throw tcu::NotSupportedError("Not enough compute workgroup invocations supported.");
232 
233     // Create src and dst images
234     {
235         vk::VkImageUsageFlags imageUsageFlags = vk::VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
236                                                 vk::VK_IMAGE_USAGE_TRANSFER_DST_BIT | vk::VK_IMAGE_USAGE_SAMPLED_BIT |
237                                                 vk::VK_IMAGE_USAGE_STORAGE_BIT;
238 
239         imageSrc = createImage2D(ctx, PROTECTION_ENABLED, queueFamilyIndex, m_params.imageWidth, m_params.imageHeight,
240                                  vk::VK_FORMAT_R8G8B8A8_UNORM, imageUsageFlags);
241 
242         imageDst = createImage2D(ctx, PROTECTION_ENABLED, queueFamilyIndex, m_params.imageWidth, m_params.imageHeight,
243                                  vk::VK_FORMAT_R8G8B8A8_UNORM, imageUsageFlags);
244     }
245 
246     // Upload source image
247     {
248         de::MovePtr<vk::ImageWithMemory> unprotectedImage = createImage2D(
249             ctx, PROTECTION_DISABLED, queueFamilyIndex, m_params.imageWidth, m_params.imageHeight,
250             vk::VK_FORMAT_R8G8B8A8_UNORM, vk::VK_IMAGE_USAGE_TRANSFER_SRC_BIT | vk::VK_IMAGE_USAGE_TRANSFER_DST_BIT);
251 
252         // Upload data to an unprotected image
253         uploadImage(m_protectedContext, **unprotectedImage, *texture2D);
254 
255         // Copy unprotected image to protected image
256         copyToProtectedImage(m_protectedContext, **unprotectedImage, **imageSrc, vk::VK_IMAGE_LAYOUT_GENERAL,
257                              m_params.imageWidth, m_params.imageHeight);
258     }
259 
260     // Clear dst image
261     clearImage(m_protectedContext, **imageDst);
262 
263     // Create descriptors
264     {
265         vk::DescriptorSetLayoutBuilder layoutBuilder;
266         vk::DescriptorPoolBuilder poolBuilder;
267 
268         layoutBuilder.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, vk::VK_SHADER_STAGE_COMPUTE_BIT);
269         layoutBuilder.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, vk::VK_SHADER_STAGE_COMPUTE_BIT);
270         poolBuilder.addType(vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 2u);
271 
272         descriptorSetLayout = layoutBuilder.build(vk, device);
273         descriptorPool      = poolBuilder.build(vk, device, vk::VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
274         descriptorSet       = makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout);
275     }
276 
277     // Create pipeline layout
278     vk::Unique<vk::VkPipelineLayout> pipelineLayout(makePipelineLayout(vk, device, *descriptorSetLayout));
279 
280     // Create image views
281     {
282         imageViewSrc = createImageView(ctx, **imageSrc, vk::VK_FORMAT_R8G8B8A8_UNORM);
283         imageViewDst = createImageView(ctx, **imageDst, vk::VK_FORMAT_R8G8B8A8_UNORM);
284     }
285 
286     // Update descriptor set information
287     {
288         vk::DescriptorSetUpdateBuilder updateBuilder;
289 
290         vk::VkDescriptorImageInfo descStorageImgDst =
291             makeDescriptorImageInfo((vk::VkSampler)0, *imageViewDst, vk::VK_IMAGE_LAYOUT_GENERAL);
292         vk::VkDescriptorImageInfo descStorageImgSrc =
293             makeDescriptorImageInfo((vk::VkSampler)0, *imageViewSrc, vk::VK_IMAGE_LAYOUT_GENERAL);
294 
295         updateBuilder.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(0u),
296                                   vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &descStorageImgDst);
297         updateBuilder.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(1u),
298                                   vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &descStorageImgSrc);
299 
300         updateBuilder.update(vk, device);
301     }
302 
303     // Create compute commands & submit
304     {
305         const vk::Unique<vk::VkFence> fence(vk::createFence(vk, device));
306         vk::Unique<vk::VkPipeline> pipeline(makeComputePipeline(vk, device, *pipelineLayout, *computeShader));
307         vk::Unique<vk::VkCommandBuffer> cmdBuffer(
308             vk::allocateCommandBuffer(vk, device, *cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY));
309 
310         beginCommandBuffer(vk, *cmdBuffer);
311 
312         vk.cmdBindPipeline(*cmdBuffer, vk::VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
313         vk.cmdBindDescriptorSets(*cmdBuffer, vk::VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0u, 1u,
314                                  &*descriptorSet, 0u, DE_NULL);
315         vk.cmdDispatch(*cmdBuffer, 1u, 1u, 1u);
316         endCommandBuffer(vk, *cmdBuffer);
317 
318         VK_CHECK(queueSubmit(ctx, PROTECTION_ENABLED, queue, *cmdBuffer, *fence, ~0ull));
319     }
320 
321     // Calculate reference image
322     calculateRef(*texture2D);
323 
324     // Validate result
325     return validateResult(**imageDst, vk::VK_IMAGE_LAYOUT_GENERAL, *texture2D, refSampler);
326 }
327 
calculateRef(tcu::Texture2D & texture2D)328 void WorkgroupStorageTestInstance::calculateRef(tcu::Texture2D &texture2D)
329 {
330     const tcu::PixelBufferAccess &reference = texture2D.getLevel(0);
331 
332     std::vector<tcu::IVec4> sharedData(m_params.sharedMemorySize);
333     for (uint32_t dataIdx = 0; dataIdx < m_params.sharedMemorySize; ++dataIdx)
334         sharedData[dataIdx] = reference.getPixelInt(dataIdx % reference.getWidth(), dataIdx / reference.getWidth());
335 
336     for (int x = 0; x < reference.getWidth(); ++x)
337         for (int y = 0; y < reference.getHeight(); ++y)
338         {
339             const int idx = (y * reference.getWidth() + x + 1) % m_params.sharedMemorySize;
340 
341             reference.setPixel(sharedData[idx], x, y);
342         }
343 }
344 
validateResult(vk::VkImage image,vk::VkImageLayout imageLayout,const tcu::Texture2D & texture2D,const tcu::Sampler & refSampler)345 tcu::TestStatus WorkgroupStorageTestInstance::validateResult(vk::VkImage image, vk::VkImageLayout imageLayout,
346                                                              const tcu::Texture2D &texture2D,
347                                                              const tcu::Sampler &refSampler)
348 {
349     de::Random rnd(getSeedValue(m_params));
350     ValidationData refData;
351 
352     for (int ndx = 0; ndx < 4; ++ndx)
353     {
354         const float lod = 0.0f;
355         const float cx  = rnd.getFloat(0.0f, 1.0f);
356         const float cy  = rnd.getFloat(0.0f, 1.0f);
357 
358         refData.coords[ndx] = tcu::Vec4(cx, cy, 0.0f, 0.0f);
359         refData.values[ndx] = texture2D.sample(refSampler, cx, cy, lod);
360     }
361 
362     if (!m_validator.validateImage(m_protectedContext, refData, image, vk::VK_FORMAT_R8G8B8A8_UNORM, imageLayout))
363         return tcu::TestStatus::fail("Result validation failed");
364     else
365         return tcu::TestStatus::pass("Pass");
366 }
367 
368 } // namespace
369 
createWorkgroupStorageTests(tcu::TestContext & testCtx)370 tcu::TestCaseGroup *createWorkgroupStorageTests(tcu::TestContext &testCtx)
371 {
372     de::MovePtr<tcu::TestCaseGroup> workgroupGroup(new tcu::TestCaseGroup(testCtx, "workgroupstorage"));
373 
374     static const uint32_t sharedMemSizes[] = {1, 4, 5, 60, 101, 503};
375 
376     for (int sharedMemSizeIdx = 0; sharedMemSizeIdx < DE_LENGTH_OF_ARRAY(sharedMemSizes); ++sharedMemSizeIdx)
377     {
378         std::string testName = std::string("memsize_") + de::toString(sharedMemSizes[sharedMemSizeIdx]);
379         workgroupGroup->addChild(
380             new WorkgroupStorageTestCase(testCtx, testName, Params(sharedMemSizes[sharedMemSizeIdx])));
381     }
382 
383     return workgroupGroup.release();
384 }
385 
386 } // namespace ProtectedMem
387 } // namespace vkt
388