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