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 deUint32 sharedMemorySize;
61 deUint32 imageWidth;
62 deUint32 imageHeight;
63
Paramsvkt::ProtectedMem::__anone9abc4c50111::Params64 Params (deUint32 sharedMemorySize_)
65 : sharedMemorySize (sharedMemorySize_)
66 {
67 // Find suitable image dimensions based on shared memory size
68 imageWidth = 1;
69 imageHeight = 1;
70 bool increaseWidth = true;
71 while (imageWidth * imageHeight < sharedMemorySize)
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 deUint32 getSeedValue (const Params& params)
84 {
85 return deInt32Hash(params.sharedMemorySize);
86 }
87
88 class WorkgroupStorageTestInstance : public ProtectedTestInstance
89 {
90 public:
91 WorkgroupStorageTestInstance (Context& ctx,
92 const ImageValidator& validator,
93 const Params& params);
94 virtual tcu::TestStatus iterate (void);
95
96 private:
97 de::MovePtr<tcu::Texture2D> createTestTexture2D (void);
98 tcu::TestStatus validateResult (vk::VkImage image,
99 vk::VkImageLayout imageLayout,
100 const tcu::Texture2D& texture2D,
101 const tcu::Sampler& refSampler);
102 void calculateRef (tcu::Texture2D& texture2D);
103
104 const ImageValidator& m_validator;
105 const Params& m_params;
106 };
107
108 class WorkgroupStorageTestCase : public TestCase
109 {
110 public:
WorkgroupStorageTestCase(tcu::TestContext & testCtx,const std::string & name,const Params & params)111 WorkgroupStorageTestCase (tcu::TestContext& testCtx,
112 const std::string& name,
113 const Params& params)
114 : TestCase (testCtx, name)
115 , m_validator (vk::VK_FORMAT_R8G8B8A8_UNORM)
116 , m_params (params)
117 {
118 }
119
~WorkgroupStorageTestCase(void)120 virtual ~WorkgroupStorageTestCase (void) {}
createInstance(Context & ctx) const121 virtual TestInstance* createInstance (Context& ctx) const
122 {
123 return new WorkgroupStorageTestInstance(ctx, m_validator, m_params);
124 }
125 virtual void initPrograms (vk::SourceCollections& programCollection) const;
checkSupport(Context & context) const126 virtual void checkSupport (Context& context) const
127 {
128 checkProtectedQueueSupport(context);
129 }
130
131 private:
132 ImageValidator m_validator;
133 Params m_params;
134 };
135
initPrograms(vk::SourceCollections & programCollection) const136 void WorkgroupStorageTestCase::initPrograms (vk::SourceCollections& programCollection) const
137 {
138 m_validator.initPrograms(programCollection);
139
140 // Fill shared data array with source image data. Output result color with results from
141 // shared memory written by another invocation.
142 std::string comp =
143 std::string() +
144 "#version 450\n"
145 "layout(local_size_x = " + de::toString(m_params.imageWidth) + ", local_size_y = " + de::toString(m_params.imageHeight) + ", local_size_z = 1) in;\n"
146 "layout(set = 0, binding = 0, rgba8) writeonly uniform highp image2D u_resultImage;\n"
147 "layout(set = 0, binding = 1, rgba8) readonly uniform highp image2D u_srcImage;\n"
148 "shared vec4 sharedData[" + de::toString(m_params.sharedMemorySize) + "];\n"
149 "\n"
150 "void main() {\n"
151 " int gx = int(gl_GlobalInvocationID.x);\n"
152 " int gy = int(gl_GlobalInvocationID.y);\n"
153 " int s = " + de::toString(m_params.sharedMemorySize) + ";\n"
154 " int idx0 = gy * " + de::toString(m_params.imageWidth) + " + gx;\n"
155 " int idx1 = (idx0 + 1) % s;\n"
156 " vec4 color = imageLoad(u_srcImage, ivec2(gx, gy));\n"
157 " if (idx0 < s)\n"
158 " {\n"
159 " sharedData[idx0] = color;\n"
160 " }\n"
161 " barrier();\n"
162 " vec4 outColor = sharedData[idx1];\n"
163 " imageStore(u_resultImage, ivec2(gx, gy), outColor);\n"
164 "}\n";
165
166 programCollection.glslSources.add("comp") << glu::ComputeSource(comp);
167 }
168
WorkgroupStorageTestInstance(Context & ctx,const ImageValidator & validator,const Params & params)169 WorkgroupStorageTestInstance::WorkgroupStorageTestInstance (Context& ctx,
170 const ImageValidator& validator,
171 const Params& params)
172 : ProtectedTestInstance (ctx)
173 , m_validator (validator)
174 , m_params (params)
175 {
176 }
177
createTestTexture2D(void)178 de::MovePtr<tcu::Texture2D> WorkgroupStorageTestInstance::createTestTexture2D (void)
179 {
180 const tcu::TextureFormat texFmt = mapVkFormat(vk::VK_FORMAT_R8G8B8A8_UNORM);
181 const tcu::TextureFormatInfo fmtInfo = tcu::getTextureFormatInfo(texFmt);
182 de::MovePtr<tcu::Texture2D> texture2D (new tcu::Texture2D(texFmt, m_params.imageWidth, m_params.imageHeight));
183
184 texture2D->allocLevel(0);
185
186 const tcu::PixelBufferAccess& level = texture2D->getLevel(0);
187
188 fillWithRandomColorTiles(level, fmtInfo.valueMin, fmtInfo.valueMax, getSeedValue(m_params));
189
190 return texture2D;
191 }
192
iterate(void)193 tcu::TestStatus WorkgroupStorageTestInstance::iterate (void)
194 {
195 ProtectedContext& ctx (m_protectedContext);
196 const vk::DeviceInterface& vk = ctx.getDeviceInterface();
197 const vk::VkDevice device = ctx.getDevice();
198 const vk::VkQueue queue = ctx.getQueue();
199 const deUint32 queueFamilyIndex = ctx.getQueueFamilyIndex();
200 const vk::VkPhysicalDeviceProperties properties = vk::getPhysicalDeviceProperties(ctx.getInstanceDriver(), ctx.getPhysicalDevice());
201
202 vk::Unique<vk::VkCommandPool> cmdPool (makeCommandPool(vk, device, PROTECTION_ENABLED, queueFamilyIndex));
203
204 de::MovePtr<tcu::Texture2D> texture2D = createTestTexture2D();
205 const tcu::Sampler refSampler = tcu::Sampler(tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::CLAMP_TO_EDGE,
206 tcu::Sampler::NEAREST, tcu::Sampler::NEAREST,
207 00.0f /* LOD threshold */, true /* normalized coords */, tcu::Sampler::COMPAREMODE_NONE,
208 0 /* cmp channel */, tcu::Vec4(0.0f) /* border color */, true /* seamless cube map */);
209
210 vk::Unique<vk::VkShaderModule> computeShader (vk::createShaderModule(vk, device, ctx.getBinaryCollection().get("comp"), 0));
211
212 de::MovePtr<vk::ImageWithMemory> imageSrc;
213 de::MovePtr<vk::ImageWithMemory> imageDst;
214 vk::Move<vk::VkSampler> sampler;
215 vk::Move<vk::VkImageView> imageViewSrc;
216 vk::Move<vk::VkImageView> imageViewDst;
217
218 vk::Move<vk::VkDescriptorSetLayout> descriptorSetLayout;
219 vk::Move<vk::VkDescriptorPool> descriptorPool;
220 vk::Move<vk::VkDescriptorSet> descriptorSet;
221
222 // Check there is enough shared memory supported
223 if (properties.limits.maxComputeSharedMemorySize < m_params.sharedMemorySize * 4 * 4)
224 throw tcu::NotSupportedError("Not enough shared memory supported.");
225
226 // Check the number of invocations supported
227 if (properties.limits.maxComputeWorkGroupInvocations < m_params.imageWidth * m_params.imageHeight)
228 throw tcu::NotSupportedError("Not enough compute workgroup invocations supported.");
229
230 // Create src and dst images
231 {
232 vk::VkImageUsageFlags imageUsageFlags = vk::VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
233 vk::VK_IMAGE_USAGE_TRANSFER_DST_BIT |
234 vk::VK_IMAGE_USAGE_SAMPLED_BIT |
235 vk::VK_IMAGE_USAGE_STORAGE_BIT;
236
237 imageSrc = createImage2D(ctx, PROTECTION_ENABLED, queueFamilyIndex,
238 m_params.imageWidth, m_params.imageHeight,
239 vk::VK_FORMAT_R8G8B8A8_UNORM,
240 imageUsageFlags);
241
242 imageDst = createImage2D(ctx, PROTECTION_ENABLED, queueFamilyIndex,
243 m_params.imageWidth, m_params.imageHeight,
244 vk::VK_FORMAT_R8G8B8A8_UNORM,
245 imageUsageFlags);
246 }
247
248 // Upload source image
249 {
250 de::MovePtr<vk::ImageWithMemory> unprotectedImage = createImage2D(ctx, PROTECTION_DISABLED, queueFamilyIndex,
251 m_params.imageWidth, m_params.imageHeight,
252 vk::VK_FORMAT_R8G8B8A8_UNORM,
253 vk::VK_IMAGE_USAGE_TRANSFER_SRC_BIT | vk::VK_IMAGE_USAGE_TRANSFER_DST_BIT);
254
255 // Upload data to an unprotected image
256 uploadImage(m_protectedContext, **unprotectedImage, *texture2D);
257
258 // Copy unprotected image to protected image
259 copyToProtectedImage(m_protectedContext, **unprotectedImage, **imageSrc, vk::VK_IMAGE_LAYOUT_GENERAL, m_params.imageWidth, m_params.imageHeight);
260 }
261
262 // Clear dst image
263 clearImage(m_protectedContext, **imageDst);
264
265 // Create descriptors
266 {
267 vk::DescriptorSetLayoutBuilder layoutBuilder;
268 vk::DescriptorPoolBuilder poolBuilder;
269
270 layoutBuilder.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, vk::VK_SHADER_STAGE_COMPUTE_BIT);
271 layoutBuilder.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, vk::VK_SHADER_STAGE_COMPUTE_BIT);
272 poolBuilder.addType(vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 2u);
273
274 descriptorSetLayout = layoutBuilder.build(vk, device);
275 descriptorPool = poolBuilder.build(vk, device, vk::VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
276 descriptorSet = makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout);
277 }
278
279 // Create pipeline layout
280 vk::Unique<vk::VkPipelineLayout> pipelineLayout (makePipelineLayout(vk, device, *descriptorSetLayout));
281
282 // Create image views
283 {
284 imageViewSrc = createImageView(ctx, **imageSrc, vk::VK_FORMAT_R8G8B8A8_UNORM);
285 imageViewDst = createImageView(ctx, **imageDst, vk::VK_FORMAT_R8G8B8A8_UNORM);
286 }
287
288 // Update descriptor set information
289 {
290 vk::DescriptorSetUpdateBuilder updateBuilder;
291
292 vk::VkDescriptorImageInfo descStorageImgDst = makeDescriptorImageInfo((vk::VkSampler)0, *imageViewDst, vk::VK_IMAGE_LAYOUT_GENERAL);
293 vk::VkDescriptorImageInfo descStorageImgSrc = makeDescriptorImageInfo((vk::VkSampler)0, *imageViewSrc, vk::VK_IMAGE_LAYOUT_GENERAL);
294
295 updateBuilder.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(0u), vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &descStorageImgDst);
296 updateBuilder.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(1u), vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &descStorageImgSrc);
297
298 updateBuilder.update(vk, device);
299 }
300
301 // Create compute commands & submit
302 {
303 const vk::Unique<vk::VkFence> fence (vk::createFence(vk, device));
304 vk::Unique<vk::VkPipeline> pipeline (makeComputePipeline(vk, device, *pipelineLayout, *computeShader));
305 vk::Unique<vk::VkCommandBuffer> cmdBuffer (vk::allocateCommandBuffer(vk, device, *cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY));
306
307 beginCommandBuffer(vk, *cmdBuffer);
308
309 vk.cmdBindPipeline(*cmdBuffer, vk::VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
310 vk.cmdBindDescriptorSets(*cmdBuffer, vk::VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0u, 1u, &*descriptorSet, 0u, DE_NULL);
311 vk.cmdDispatch(*cmdBuffer, 1u, 1u, 1u);
312 endCommandBuffer(vk, *cmdBuffer);
313
314 VK_CHECK(queueSubmit(ctx, PROTECTION_ENABLED, queue, *cmdBuffer, *fence, ~0ull));
315 }
316
317 // Calculate reference image
318 calculateRef(*texture2D);
319
320 // Validate result
321 return validateResult(**imageDst, vk::VK_IMAGE_LAYOUT_GENERAL, *texture2D, refSampler);
322 }
323
calculateRef(tcu::Texture2D & texture2D)324 void WorkgroupStorageTestInstance::calculateRef (tcu::Texture2D& texture2D)
325 {
326 const tcu::PixelBufferAccess& reference = texture2D.getLevel(0);
327
328 std::vector<tcu::IVec4> sharedData(m_params.sharedMemorySize);
329 for (deUint32 dataIdx = 0; dataIdx < m_params.sharedMemorySize; ++dataIdx)
330 sharedData[dataIdx] = reference.getPixelInt(dataIdx % reference.getWidth(), dataIdx / reference.getWidth());
331
332 for (int x = 0; x < reference.getWidth(); ++x)
333 for (int y = 0; y < reference.getHeight(); ++y)
334 {
335 const int idx = (y * reference.getWidth() + x + 1) % m_params.sharedMemorySize;
336
337 reference.setPixel(sharedData[idx], x, y);
338 }
339 }
340
validateResult(vk::VkImage image,vk::VkImageLayout imageLayout,const tcu::Texture2D & texture2D,const tcu::Sampler & refSampler)341 tcu::TestStatus WorkgroupStorageTestInstance::validateResult (vk::VkImage image, vk::VkImageLayout imageLayout, const tcu::Texture2D& texture2D, const tcu::Sampler& refSampler)
342 {
343 de::Random rnd (getSeedValue(m_params));
344 ValidationData refData;
345
346 for (int ndx = 0; ndx < 4; ++ndx)
347 {
348 const float lod = 0.0f;
349 const float cx = rnd.getFloat(0.0f, 1.0f);
350 const float cy = rnd.getFloat(0.0f, 1.0f);
351
352 refData.coords[ndx] = tcu::Vec4(cx, cy, 0.0f, 0.0f);
353 refData.values[ndx] = texture2D.sample(refSampler, cx, cy, lod);
354 }
355
356 if (!m_validator.validateImage(m_protectedContext, refData, image, vk::VK_FORMAT_R8G8B8A8_UNORM, imageLayout))
357 return tcu::TestStatus::fail("Result validation failed");
358 else
359 return tcu::TestStatus::pass("Pass");
360 }
361
362 } // anonymous
363
createWorkgroupStorageTests(tcu::TestContext & testCtx)364 tcu::TestCaseGroup* createWorkgroupStorageTests (tcu::TestContext& testCtx)
365 {
366 de::MovePtr<tcu::TestCaseGroup> workgroupGroup (new tcu::TestCaseGroup(testCtx, "workgroupstorage"));
367
368 static const deUint32 sharedMemSizes[] = { 1, 4, 5, 60, 101, 503 };
369
370 for (int sharedMemSizeIdx = 0; sharedMemSizeIdx < DE_LENGTH_OF_ARRAY(sharedMemSizes); ++sharedMemSizeIdx)
371 {
372 std::string testName = std::string("memsize_") + de::toString(sharedMemSizes[sharedMemSizeIdx]);
373 workgroupGroup->addChild(new WorkgroupStorageTestCase(testCtx, testName, Params(sharedMemSizes[sharedMemSizeIdx])));
374 }
375
376 return workgroupGroup.release();
377 }
378
379 } // ProtectedMem
380 } // vkt
381