1 /*------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2019 The Khronos Group Inc.
6  * Copyright (c) 2018 The Khronos Group Inc.
7  * Copyright (c) 2018 Google Inc.
8  * Copyright (c) 2017 Samsung Electronics Co., Ltd.
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 Protected memory stack tests
25  *//*--------------------------------------------------------------------*/
26 
27 #include "vktProtectedMemStackTests.hpp"
28 
29 #include "vktProtectedMemContext.hpp"
30 #include "vktProtectedMemUtils.hpp"
31 #include "vktProtectedMemImageValidator.hpp"
32 #include "vktTestCase.hpp"
33 #include "vktTestGroupUtil.hpp"
34 
35 #include "vkPrograms.hpp"
36 #include "vkTypeUtil.hpp"
37 #include "vkBuilderUtil.hpp"
38 #include "vkImageUtil.hpp"
39 #include "vkCmdUtil.hpp"
40 #include "vkObjUtil.hpp"
41 
42 #include "tcuTestLog.hpp"
43 #include "tcuVector.hpp"
44 #include "tcuTextureUtil.hpp"
45 #include "tcuStringTemplate.hpp"
46 
47 #include "gluTextureTestUtil.hpp"
48 
49 #include "deRandom.hpp"
50 
51 namespace vkt
52 {
53 namespace ProtectedMem
54 {
55 
56 namespace
57 {
58 
59 struct Params
60 {
61     uint32_t stackSize;
62     uint32_t imageWidth;
63     uint32_t imageHeight;
64 
Paramsvkt::ProtectedMem::__anon654642cd0111::Params65     Params(uint32_t stackSize_) : stackSize(stackSize_)
66     {
67         // Find suitable image dimensions based on stack memory size
68         imageWidth         = 1;
69         imageHeight        = 1;
70         bool increaseWidth = true;
71         while (imageWidth * imageHeight < stackSize)
72         {
73             if (increaseWidth)
74                 imageWidth *= 2;
75             else
76                 imageHeight *= 2;
77 
78             increaseWidth = !increaseWidth;
79         }
80     }
81 };
82 
getSeedValue(const Params & params)83 uint32_t getSeedValue(const Params &params)
84 {
85     return deInt32Hash(params.stackSize);
86 }
87 
88 class StackTestInstance : public ProtectedTestInstance
89 {
90 public:
91     StackTestInstance(Context &ctx, const ImageValidator &validator, const Params &params);
92     virtual tcu::TestStatus iterate(void);
93 
94 private:
95     de::MovePtr<tcu::Texture2D> createTestTexture2D(void);
96     bool validateResult(vk::VkImage image, vk::VkImageLayout imageLayout, const tcu::Texture2D &texture2D,
97                         const tcu::Sampler &refSampler);
98     void calculateRef(tcu::Texture2D &texture2D);
99 
100     const ImageValidator &m_validator;
101     const Params &m_params;
102 };
103 
104 class StackTestCase : public TestCase
105 {
106 public:
StackTestCase(tcu::TestContext & testCtx,const std::string & name,const Params & params)107     StackTestCase(tcu::TestContext &testCtx, const std::string &name, const Params &params)
108         : TestCase(testCtx, name)
109         , m_validator(vk::VK_FORMAT_R8G8B8A8_UNORM)
110         , m_params(params)
111     {
112     }
113 
~StackTestCase(void)114     virtual ~StackTestCase(void)
115     {
116     }
createInstance(Context & ctx) const117     virtual TestInstance *createInstance(Context &ctx) const
118     {
119         return new StackTestInstance(ctx, m_validator, m_params);
120     }
121     virtual void initPrograms(vk::SourceCollections &programCollection) const;
122 
123 private:
124     ImageValidator m_validator;
125     Params m_params;
126 };
127 
initPrograms(vk::SourceCollections & programCollection) const128 void StackTestCase::initPrograms(vk::SourceCollections &programCollection) const
129 {
130     m_validator.initPrograms(programCollection);
131 
132     // Test validates handling of protected memory allocated on stack.
133     // The test copies protected memory content into temporary variable allocated inside function p.
134     // Thus test forces protected content to appear on stack.
135     // Function p() returns specified protected memory element from the variable allocated on stack.
136     // Function u() returns specified protected memory element from the global variable.
137     // Values returned by p() and u() should be same.
138     // Test is repeated 2 times () in shader to avoid coincidental matches.
139     // In case of any mismatches it is signalized to inherited verifier function by setting 0 in result store image.
140     // Each invocation validates particular element (bytes) on stack.
141     // Number of invocations matches stack size specified in test parameters.
142     std::string comp = std::string() +
143                        "#version 450\n"
144                        "layout(local_size_x = " +
145                        de::toString(m_params.imageWidth) + ", local_size_y = " + de::toString(m_params.imageHeight) +
146                        ", local_size_z = 1) in;\n"
147                        "layout(set = 0, binding = 0, rgba8) writeonly uniform highp image2D u_resultImage;\n"
148                        "layout(set = 0, binding = 1, rgba8) readonly uniform highp image2D u_srcImage;\n"
149                        "vec4 protectedData[" +
150                        de::toString(m_params.stackSize) +
151                        "];\n"
152                        "\n"
153                        "vec4 p(int idx)\n"
154                        "{\n"
155                        "    vec4 localData[" +
156                        de::toString(m_params.stackSize) +
157                        "];\n"
158                        "    for (int i = 0; i < " +
159                        de::toString(m_params.stackSize) +
160                        "; i++)\n"
161                        "        localData[i] = protectedData[i];\n"
162                        "    return localData[idx];\n"
163                        "}\n"
164                        "\n"
165                        "vec4 u(int idx)\n"
166                        "{\n"
167                        "    return protectedData[idx];\n"
168                        "}\n"
169                        "\n"
170                        "void main() {\n"
171                        "    const int n = " +
172                        de::toString(m_params.stackSize) +
173                        ";\n"
174                        "    int m = 0;\n"
175                        "    int w = " +
176                        de::toString(m_params.imageWidth) +
177                        ";\n"
178                        "    int gx = int(gl_GlobalInvocationID.x);\n"
179                        "    int gy = int(gl_GlobalInvocationID.y);\n"
180                        "    int checked_ndx = gy * w + gx;\n"
181                        "    vec4 outColor;\n"
182                        "\n"
183                        "    for (int j = 0; j < 2; j++)\n"
184                        "    {\n"
185                        "        for (int i = 0; i < n; i++)\n"
186                        "        {\n"
187                        "            const int idx = (i + j) % n;\n"
188                        "            protectedData[i] = imageLoad(u_srcImage, ivec2(idx % w, idx / w));\n"
189                        "        }\n"
190                        "\n"
191                        "        vec4 vp = p(checked_ndx);\n"
192                        "        vec4 vu = u(checked_ndx);\n"
193                        "        if (any(notEqual(vp,vu)))\n"
194                        "            m++;\n"
195                        "    }\n"
196                        "\n"
197                        "    if (m <= 0)\n"
198                        "        outColor = vec4(0.0f);\n"
199                        "    else\n"
200                        "        outColor = vec4(1.0f);\n"
201                        "    imageStore(u_resultImage, ivec2(gx, gy), outColor);\n"
202                        "}\n";
203 
204     programCollection.glslSources.add("comp") << glu::ComputeSource(comp);
205 }
206 
StackTestInstance(Context & ctx,const ImageValidator & validator,const Params & params)207 StackTestInstance::StackTestInstance(Context &ctx, const ImageValidator &validator, const Params &params)
208     : ProtectedTestInstance(ctx)
209     , m_validator(validator)
210     , m_params(params)
211 {
212 }
213 
createTestTexture2D(void)214 de::MovePtr<tcu::Texture2D> StackTestInstance::createTestTexture2D(void)
215 {
216     const tcu::TextureFormat texFmt = mapVkFormat(vk::VK_FORMAT_R8G8B8A8_UNORM);
217     de::MovePtr<tcu::Texture2D> texture2D(new tcu::Texture2D(texFmt, m_params.imageWidth, m_params.imageHeight));
218 
219     texture2D->allocLevel(0);
220 
221     const tcu::PixelBufferAccess &level = texture2D->getLevel(0);
222 
223     fillWithUniqueColors(level, getSeedValue(m_params));
224 
225     return texture2D;
226 }
227 
iterate(void)228 tcu::TestStatus StackTestInstance::iterate(void)
229 {
230     ProtectedContext &ctx(m_protectedContext);
231     const vk::DeviceInterface &vk   = ctx.getDeviceInterface();
232     const vk::VkDevice device       = ctx.getDevice();
233     const vk::VkQueue queue         = ctx.getQueue();
234     const uint32_t queueFamilyIndex = ctx.getQueueFamilyIndex();
235     const vk::VkPhysicalDeviceProperties properties =
236         vk::getPhysicalDeviceProperties(ctx.getInstanceDriver(), ctx.getPhysicalDevice());
237 
238     vk::Unique<vk::VkCommandPool> cmdPool(makeCommandPool(vk, device, PROTECTION_ENABLED, queueFamilyIndex));
239 
240     de::MovePtr<tcu::Texture2D> texture2D = createTestTexture2D();
241     const tcu::Sampler refSampler         = tcu::Sampler(
242         tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::NEAREST,
243         tcu::Sampler::NEAREST, 00.0f /* LOD threshold */, true /* normalized coords */, tcu::Sampler::COMPAREMODE_NONE,
244         0 /* cmp channel */, tcu::Vec4(0.0f) /* border color */, true /* seamless cube map */);
245 
246     vk::Unique<vk::VkShaderModule> computeShader(
247         vk::createShaderModule(vk, device, ctx.getBinaryCollection().get("comp"), 0));
248 
249     de::MovePtr<vk::ImageWithMemory> imageSrc;
250     de::MovePtr<vk::ImageWithMemory> imageDst;
251     vk::Move<vk::VkSampler> sampler;
252     vk::Move<vk::VkImageView> imageViewSrc;
253     vk::Move<vk::VkImageView> imageViewDst;
254 
255     vk::Move<vk::VkDescriptorSetLayout> descriptorSetLayout;
256     vk::Move<vk::VkDescriptorPool> descriptorPool;
257     vk::Move<vk::VkDescriptorSet> descriptorSet;
258 
259     // Check the number of invocations supported
260     if (properties.limits.maxComputeWorkGroupInvocations < m_params.imageWidth * m_params.imageHeight)
261         throw tcu::NotSupportedError("Not enough compute workgroup invocations supported.");
262 
263     // Create src and dst images
264     {
265         vk::VkImageUsageFlags imageUsageFlags = vk::VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
266                                                 vk::VK_IMAGE_USAGE_TRANSFER_DST_BIT | vk::VK_IMAGE_USAGE_SAMPLED_BIT |
267                                                 vk::VK_IMAGE_USAGE_STORAGE_BIT;
268 
269         imageSrc = createImage2D(ctx, PROTECTION_ENABLED, queueFamilyIndex, m_params.imageWidth, m_params.imageHeight,
270                                  vk::VK_FORMAT_R8G8B8A8_UNORM, imageUsageFlags);
271 
272         imageDst = createImage2D(ctx, PROTECTION_ENABLED, queueFamilyIndex, m_params.imageWidth, m_params.imageHeight,
273                                  vk::VK_FORMAT_R8G8B8A8_UNORM, imageUsageFlags);
274     }
275 
276     // Upload source image
277     {
278         de::MovePtr<vk::ImageWithMemory> unprotectedImage = createImage2D(
279             ctx, PROTECTION_DISABLED, queueFamilyIndex, m_params.imageWidth, m_params.imageHeight,
280             vk::VK_FORMAT_R8G8B8A8_UNORM, vk::VK_IMAGE_USAGE_TRANSFER_SRC_BIT | vk::VK_IMAGE_USAGE_TRANSFER_DST_BIT);
281 
282         // Upload data to an unprotected image
283         uploadImage(m_protectedContext, **unprotectedImage, *texture2D);
284 
285         // Copy unprotected image to protected image
286         copyToProtectedImage(m_protectedContext, **unprotectedImage, **imageSrc, vk::VK_IMAGE_LAYOUT_GENERAL,
287                              m_params.imageWidth, m_params.imageHeight);
288     }
289 
290     // Clear dst image
291     clearImage(m_protectedContext, **imageDst);
292 
293     // Create descriptors
294     {
295         vk::DescriptorSetLayoutBuilder layoutBuilder;
296         vk::DescriptorPoolBuilder poolBuilder;
297 
298         layoutBuilder.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, vk::VK_SHADER_STAGE_COMPUTE_BIT);
299         layoutBuilder.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, vk::VK_SHADER_STAGE_COMPUTE_BIT);
300         poolBuilder.addType(vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 2u);
301 
302         descriptorSetLayout = layoutBuilder.build(vk, device);
303         descriptorPool      = poolBuilder.build(vk, device, vk::VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
304         descriptorSet       = makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout);
305     }
306 
307     // Create pipeline layout
308     vk::Unique<vk::VkPipelineLayout> pipelineLayout(makePipelineLayout(vk, device, *descriptorSetLayout));
309 
310     // Create image views
311     {
312         imageViewSrc = createImageView(ctx, **imageSrc, vk::VK_FORMAT_R8G8B8A8_UNORM);
313         imageViewDst = createImageView(ctx, **imageDst, vk::VK_FORMAT_R8G8B8A8_UNORM);
314     }
315 
316     // Update descriptor set information
317     {
318         vk::DescriptorSetUpdateBuilder updateBuilder;
319 
320         vk::VkDescriptorImageInfo descStorageImgDst =
321             makeDescriptorImageInfo((vk::VkSampler)0, *imageViewDst, vk::VK_IMAGE_LAYOUT_GENERAL);
322         vk::VkDescriptorImageInfo descStorageImgSrc =
323             makeDescriptorImageInfo((vk::VkSampler)0, *imageViewSrc, vk::VK_IMAGE_LAYOUT_GENERAL);
324 
325         updateBuilder.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(0u),
326                                   vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &descStorageImgDst);
327         updateBuilder.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(1u),
328                                   vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &descStorageImgSrc);
329 
330         updateBuilder.update(vk, device);
331     }
332 
333     // Calculate reference image
334     calculateRef(*texture2D);
335 
336     bool result = true;
337 
338     // Create compute commands & submit
339     // Command buffer load is repeated 8 times () to avoid coincidental matches.
340     for (int i = 0; (i < 8) && (result == true); i++)
341     {
342         // Protected memory operations can take a long time, touch watchdog halfway through.
343         if (i == 4)
344             m_context.getTestContext().touchWatchdog();
345 
346         const vk::Unique<vk::VkFence> fence(vk::createFence(vk, device));
347         vk::Unique<vk::VkPipeline> pipeline(makeComputePipeline(vk, device, *pipelineLayout, *computeShader));
348         vk::Unique<vk::VkCommandBuffer> cmdBuffer(
349             vk::allocateCommandBuffer(vk, device, *cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY));
350 
351         beginCommandBuffer(vk, *cmdBuffer);
352 
353         vk.cmdBindPipeline(*cmdBuffer, vk::VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
354         vk.cmdBindDescriptorSets(*cmdBuffer, vk::VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0u, 1u,
355                                  &*descriptorSet, 0u, DE_NULL);
356         vk.cmdDispatch(*cmdBuffer, 1u, 1u, 1u);
357         endCommandBuffer(vk, *cmdBuffer);
358 
359         VK_CHECK(queueSubmit(ctx, PROTECTION_ENABLED, queue, *cmdBuffer, *fence, ~0ull));
360 
361         VK_CHECK(vk.waitForFences(device, 1u, &*fence, VK_TRUE, ~0ull));
362 
363         result = validateResult(**imageDst, vk::VK_IMAGE_LAYOUT_GENERAL, *texture2D, refSampler);
364     }
365 
366     if (result == true)
367         return tcu::TestStatus::pass("Pass");
368     else
369         return tcu::TestStatus::fail("Result validation failed");
370 }
371 
calculateRef(tcu::Texture2D & texture2D)372 void StackTestInstance::calculateRef(tcu::Texture2D &texture2D)
373 {
374     const tcu::PixelBufferAccess &reference = texture2D.getLevel(0);
375     const tcu::IVec4 zero;
376 
377     for (int x = 0; x < reference.getWidth(); ++x)
378         for (int y = 0; y < reference.getHeight(); ++y)
379             reference.setPixel(zero, x, y);
380 }
381 
validateResult(vk::VkImage image,vk::VkImageLayout imageLayout,const tcu::Texture2D & texture2D,const tcu::Sampler & refSampler)382 bool StackTestInstance::validateResult(vk::VkImage image, vk::VkImageLayout imageLayout,
383                                        const tcu::Texture2D &texture2D, const tcu::Sampler &refSampler)
384 {
385     de::Random rnd(getSeedValue(m_params));
386     ValidationData refData;
387 
388     for (int ndx = 0; ndx < 4; ++ndx)
389     {
390         const float lod = 0.0f;
391         const float cx  = rnd.getFloat(0.0f, 1.0f);
392         const float cy  = rnd.getFloat(0.0f, 1.0f);
393 
394         refData.coords[ndx] = tcu::Vec4(cx, cy, 0.0f, 0.0f);
395         refData.values[ndx] = texture2D.sample(refSampler, cx, cy, lod);
396     }
397 
398     if (!m_validator.validateImage(m_protectedContext, refData, image, vk::VK_FORMAT_R8G8B8A8_UNORM, imageLayout))
399         return false;
400     else
401         return true;
402 }
403 
404 } // namespace
405 
createStackTests(tcu::TestContext & testCtx)406 tcu::TestCaseGroup *createStackTests(tcu::TestContext &testCtx)
407 {
408     // Protected memory stack tests
409     de::MovePtr<tcu::TestCaseGroup> stackGroup(new tcu::TestCaseGroup(testCtx, "stack"));
410 
411     static const uint32_t stackMemSizes[] = {32, 64, 128, 256, 512, 1024};
412 
413     for (int stackMemSizeIdx = 0; stackMemSizeIdx < DE_LENGTH_OF_ARRAY(stackMemSizes); ++stackMemSizeIdx)
414     {
415         std::string testName = std::string("stacksize_") + de::toString(stackMemSizes[stackMemSizeIdx]);
416 
417         stackGroup->addChild(new StackTestCase(testCtx, testName, Params(stackMemSizes[stackMemSizeIdx])));
418     }
419 
420     return stackGroup.release();
421 }
422 
423 } // namespace ProtectedMem
424 } // namespace vkt
425