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