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