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