1 /*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2019 The Khronos Group Inc.
6 * Copyright (c) 2019 Valve Corporation.
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 *
20 *//*!
21 * \file
22 * \brief Vulkan Memory Model padding access tests
23 *//*--------------------------------------------------------------------*/
24
25 #include "vktMemoryModelPadding.hpp"
26 #include "vktTestCase.hpp"
27
28 #include "vkBufferWithMemory.hpp"
29 #include "vkBarrierUtil.hpp"
30 #include "vkObjUtil.hpp"
31 #include "vkBuilderUtil.hpp"
32 #include "vkTypeUtil.hpp"
33 #include "vkCmdUtil.hpp"
34
35 #include "deMemory.h"
36
37 namespace vkt
38 {
39 namespace MemoryModel
40 {
41
42 namespace
43 {
44 // The structures below match the shader declarations but have explicit padding members at the end so we can check their contents
45 // easily after running the shader. Using the std140 layout means structures are aligned to 16 bytes.
46
47 // Structure with a 12-byte padding at the end.
48 struct Pad12
49 {
50 int32_t a;
51 uint8_t padding[12];
52 };
53
54 // Structure with an 8-byte padding at the end.
55 struct Pad8
56 {
57 int32_t a, b;
58 uint8_t padding[8];
59 };
60
61 // Structure with a 4-byte padding at the end.
62 struct Pad4
63 {
64 int32_t a, b, c;
65 uint8_t padding[4];
66 };
67
68 // Buffer structure for the input and output buffers.
69 struct BufferStructure
70 {
71 static constexpr uint32_t kArrayLength = 3u;
72
73 Pad12 subA[kArrayLength];
74 Pad8 subB[kArrayLength];
75 Pad4 subC[kArrayLength];
76
77 // Pre-fill substructures with the given data.
BufferStructurevkt::MemoryModel::__anon26f5e7630111::BufferStructure78 BufferStructure(int32_t a, int32_t b, int32_t c, uint8_t paddingByte)
79 {
80 for (uint32_t i = 0; i < kArrayLength; ++i)
81 {
82 subA[i].a = a;
83 subB[i].a = a;
84 subC[i].a = a;
85 subB[i].b = b;
86 subC[i].b = b;
87 subC[i].c = c;
88 deMemset(subA[i].padding, static_cast<int>(paddingByte), sizeof(subA[i].padding));
89 deMemset(subB[i].padding, static_cast<int>(paddingByte), sizeof(subB[i].padding));
90 deMemset(subC[i].padding, static_cast<int>(paddingByte), sizeof(subC[i].padding));
91 }
92 }
93
94 // Pre-fill substructures with zeros.
BufferStructurevkt::MemoryModel::__anon26f5e7630111::BufferStructure95 BufferStructure(uint8_t paddingByte) : BufferStructure(0, 0, 0, paddingByte)
96 {
97 }
98
99 // Verify members and padding bytes.
checkValuesvkt::MemoryModel::__anon26f5e7630111::BufferStructure100 bool checkValues(int32_t a, int32_t b, int32_t c, uint8_t paddingByte) const
101 {
102 for (uint32_t i = 0; i < kArrayLength; ++i)
103 {
104 if (subA[i].a != a || subB[i].a != a || subC[i].a != a || subB[i].b != b || subC[i].b != b ||
105 subC[i].c != c)
106 return false;
107 }
108 return checkPaddingBytes(paddingByte);
109 }
110
111 // Verify padding bytes have a known value.
checkPaddingBytesvkt::MemoryModel::__anon26f5e7630111::BufferStructure112 bool checkPaddingBytes(uint8_t value) const
113 {
114 for (uint32_t j = 0; j < kArrayLength; ++j)
115 {
116 for (int i = 0; i < DE_LENGTH_OF_ARRAY(subA[j].padding); ++i)
117 {
118 if (subA[j].padding[i] != value)
119 return false;
120 }
121 for (int i = 0; i < DE_LENGTH_OF_ARRAY(subB[j].padding); ++i)
122 {
123 if (subB[j].padding[i] != value)
124 return false;
125 }
126 for (int i = 0; i < DE_LENGTH_OF_ARRAY(subC[j].padding); ++i)
127 {
128 if (subC[j].padding[i] != value)
129 return false;
130 }
131 }
132 return true;
133 }
134 };
135
136 class PaddingTest : public vkt::TestCase
137 {
138 public:
139 PaddingTest(tcu::TestContext &testCtx, const std::string &name);
~PaddingTest(void)140 virtual ~PaddingTest(void)
141 {
142 }
143
144 virtual void initPrograms(vk::SourceCollections &programCollection) const;
145 virtual TestInstance *createInstance(Context &context) const;
146 virtual void checkSupport(Context &context) const;
147
iterate(void)148 IterateResult iterate(void)
149 {
150 DE_ASSERT(false);
151 return STOP;
152 } // Deprecated in this module
153 };
154
155 class PaddingTestInstance : public vkt::TestInstance
156 {
157 public:
PaddingTestInstance(Context & context)158 PaddingTestInstance(Context &context) : vkt::TestInstance(context)
159 {
160 }
~PaddingTestInstance(void)161 virtual ~PaddingTestInstance(void)
162 {
163 }
164
165 virtual tcu::TestStatus iterate(void);
166 };
167
PaddingTest(tcu::TestContext & testCtx,const std::string & name)168 PaddingTest::PaddingTest(tcu::TestContext &testCtx, const std::string &name) : vkt::TestCase(testCtx, name)
169 {
170 }
171
createInstance(Context & context) const172 TestInstance *PaddingTest::createInstance(Context &context) const
173 {
174 return new PaddingTestInstance(context);
175 }
176
initPrograms(vk::SourceCollections & programCollection) const177 void PaddingTest::initPrograms(vk::SourceCollections &programCollection) const
178 {
179 const std::string arrayLenghtStr = std::to_string(BufferStructure::kArrayLength);
180
181 std::ostringstream shaderSrc;
182 shaderSrc << "#version 450\n"
183 << "#pragma use_vulkan_memory_model\n"
184 << "\n"
185 << "struct A {\n"
186 << " int a;\n"
187 << "};\n"
188 << "\n"
189 << "struct B {\n"
190 << " int a, b;\n"
191 << "};\n"
192 << "\n"
193 << "struct C {\n"
194 << " int a, b, c;\n"
195 << "};\n"
196 << "\n"
197 << "struct BufferStructure {\n"
198 << " A subA[" << arrayLenghtStr << "];\n"
199 << " B subB[" << arrayLenghtStr << "];\n"
200 << " C subC[" << arrayLenghtStr << "];\n"
201 << "};\n"
202 << "\n"
203 << "layout (set=0, binding=0, std140) uniform InputBlock\n"
204 << "{\n"
205 << " BufferStructure inBlock;\n"
206 << "};\n"
207 << "\n"
208 << "layout (set=0, binding=1, std140) buffer OutputBlock\n"
209 << "{\n"
210 << " BufferStructure outBlock;\n"
211 << "};\n"
212 << "\n"
213 << "void main()\n"
214 << "{\n"
215 << " const uint idx = gl_GlobalInvocationID.x;\n"
216 << " outBlock.subA[idx] = inBlock.subA[idx];\n"
217 << " outBlock.subB[idx] = inBlock.subB[idx];\n"
218 << " outBlock.subC[idx] = inBlock.subC[idx];\n"
219 << "}\n";
220
221 programCollection.glslSources.add("comp") << glu::ComputeSource(shaderSrc.str());
222 }
223
checkSupport(Context & context) const224 void PaddingTest::checkSupport(Context &context) const
225 {
226 context.requireDeviceFunctionality("VK_KHR_vulkan_memory_model");
227 if (!context.getVulkanMemoryModelFeatures().vulkanMemoryModel)
228 {
229 TCU_THROW(NotSupportedError, "Vulkan memory model not supported");
230 }
231 }
232
iterate(void)233 tcu::TestStatus PaddingTestInstance::iterate(void)
234 {
235 const auto &vkd = m_context.getDeviceInterface();
236 const auto device = m_context.getDevice();
237 auto &allocator = m_context.getDefaultAllocator();
238 const auto queue = m_context.getUniversalQueue();
239 const auto queueIndex = m_context.getUniversalQueueFamilyIndex();
240
241 constexpr vk::VkDeviceSize kBufferSize = static_cast<vk::VkDeviceSize>(sizeof(BufferStructure));
242 constexpr int32_t kA = 1;
243 constexpr int32_t kB = 2;
244 constexpr int32_t kC = 3;
245 constexpr uint8_t kInputPaddingByte = 0xFEu;
246 constexpr uint8_t kOutputPaddingByte = 0x7Fu;
247
248 // Create input and output buffers.
249 auto inputBufferInfo = vk::makeBufferCreateInfo(kBufferSize, vk::VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT);
250 auto outputBufferInfo = vk::makeBufferCreateInfo(kBufferSize, vk::VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
251
252 vk::BufferWithMemory inputBuffer{vkd, device, allocator, inputBufferInfo, vk::MemoryRequirement::HostVisible};
253 vk::BufferWithMemory outputBuffer{vkd, device, allocator, outputBufferInfo, vk::MemoryRequirement::HostVisible};
254
255 // Fill buffers with initial contents.
256 BufferStructure inputValues{kA, kB, kC, kInputPaddingByte};
257 BufferStructure outputInit{kOutputPaddingByte};
258
259 auto &inputAlloc = inputBuffer.getAllocation();
260 auto &outputAlloc = outputBuffer.getAllocation();
261
262 void *inputBufferPtr = static_cast<uint8_t *>(inputAlloc.getHostPtr()) + inputAlloc.getOffset();
263 void *outputBufferPtr = static_cast<uint8_t *>(outputAlloc.getHostPtr()) + outputAlloc.getOffset();
264
265 deMemcpy(inputBufferPtr, &inputValues, sizeof(inputValues));
266 deMemcpy(outputBufferPtr, &outputInit, sizeof(outputInit));
267
268 vk::flushAlloc(vkd, device, inputAlloc);
269 vk::flushAlloc(vkd, device, outputAlloc);
270
271 // Descriptor set layout.
272 vk::DescriptorSetLayoutBuilder layoutBuilder;
273 layoutBuilder.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, vk::VK_SHADER_STAGE_COMPUTE_BIT);
274 layoutBuilder.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, vk::VK_SHADER_STAGE_COMPUTE_BIT);
275 auto descriptorSetLayout = layoutBuilder.build(vkd, device);
276
277 // Descriptor pool.
278 vk::DescriptorPoolBuilder poolBuilder;
279 poolBuilder.addType(vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
280 poolBuilder.addType(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
281 auto descriptorPool = poolBuilder.build(vkd, device, vk::VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
282
283 // Descriptor set.
284 const auto descriptorSet = vk::makeDescriptorSet(vkd, device, descriptorPool.get(), descriptorSetLayout.get());
285
286 // Update descriptor set using the buffers.
287 const auto inputBufferDescriptorInfo = vk::makeDescriptorBufferInfo(inputBuffer.get(), 0ull, VK_WHOLE_SIZE);
288 const auto outputBufferDescriptorInfo = vk::makeDescriptorBufferInfo(outputBuffer.get(), 0ull, VK_WHOLE_SIZE);
289
290 vk::DescriptorSetUpdateBuilder updateBuilder;
291 updateBuilder.writeSingle(descriptorSet.get(), vk::DescriptorSetUpdateBuilder::Location::binding(0u),
292 vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &inputBufferDescriptorInfo);
293 updateBuilder.writeSingle(descriptorSet.get(), vk::DescriptorSetUpdateBuilder::Location::binding(1u),
294 vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &outputBufferDescriptorInfo);
295 updateBuilder.update(vkd, device);
296
297 // Create compute pipeline.
298 auto shaderModule = vk::createShaderModule(vkd, device, m_context.getBinaryCollection().get("comp"), 0u);
299 auto pipelineLayout = vk::makePipelineLayout(vkd, device, descriptorSetLayout.get());
300
301 const vk::VkComputePipelineCreateInfo pipelineCreateInfo = {
302 vk::VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
303 nullptr,
304 0u, // flags
305 {
306 // compute shader
307 vk::VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // VkStructureType sType;
308 nullptr, // const void* pNext;
309 0u, // VkPipelineShaderStageCreateFlags flags;
310 vk::VK_SHADER_STAGE_COMPUTE_BIT, // VkShaderStageFlagBits stage;
311 shaderModule.get(), // VkShaderModule module;
312 "main", // const char* pName;
313 nullptr, // const VkSpecializationInfo* pSpecializationInfo;
314 },
315 pipelineLayout.get(), // layout
316 DE_NULL, // basePipelineHandle
317 0, // basePipelineIndex
318 };
319 auto pipeline = vk::createComputePipeline(vkd, device, DE_NULL, &pipelineCreateInfo);
320
321 // Synchronization barriers.
322 auto inputBufferHostToDevBarrier = vk::makeBufferMemoryBarrier(
323 vk::VK_ACCESS_HOST_WRITE_BIT, vk::VK_ACCESS_SHADER_READ_BIT, inputBuffer.get(), 0ull, VK_WHOLE_SIZE);
324 auto outputBufferHostToDevBarrier = vk::makeBufferMemoryBarrier(
325 vk::VK_ACCESS_HOST_WRITE_BIT, vk::VK_ACCESS_SHADER_WRITE_BIT, outputBuffer.get(), 0ull, VK_WHOLE_SIZE);
326 auto outputBufferDevToHostBarrier = vk::makeBufferMemoryBarrier(
327 vk::VK_ACCESS_SHADER_WRITE_BIT, vk::VK_ACCESS_HOST_READ_BIT, outputBuffer.get(), 0ull, VK_WHOLE_SIZE);
328
329 // Command buffer.
330 auto cmdPool = vk::makeCommandPool(vkd, device, queueIndex);
331 auto cmdBufferPtr = vk::allocateCommandBuffer(vkd, device, cmdPool.get(), vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY);
332 auto cmdBuffer = cmdBufferPtr.get();
333
334 // Record and submit commands.
335 vk::beginCommandBuffer(vkd, cmdBuffer);
336 vkd.cmdBindPipeline(cmdBuffer, vk::VK_PIPELINE_BIND_POINT_COMPUTE, pipeline.get());
337 vkd.cmdBindDescriptorSets(cmdBuffer, vk::VK_PIPELINE_BIND_POINT_COMPUTE, pipelineLayout.get(), 0, 1u,
338 &descriptorSet.get(), 0u, nullptr);
339 vkd.cmdPipelineBarrier(cmdBuffer, vk::VK_PIPELINE_STAGE_HOST_BIT, vk::VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0u, 0u,
340 nullptr, 1u, &inputBufferHostToDevBarrier, 0u, nullptr);
341 vkd.cmdPipelineBarrier(cmdBuffer, vk::VK_PIPELINE_STAGE_HOST_BIT, vk::VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0u, 0u,
342 nullptr, 1u, &outputBufferHostToDevBarrier, 0u, nullptr);
343 vkd.cmdDispatch(cmdBuffer, BufferStructure::kArrayLength, 1u, 1u);
344 vkd.cmdPipelineBarrier(cmdBuffer, vk::VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, vk::VK_PIPELINE_STAGE_HOST_BIT, 0u, 0u,
345 nullptr, 1u, &outputBufferDevToHostBarrier, 0u, nullptr);
346 vk::endCommandBuffer(vkd, cmdBuffer);
347 vk::submitCommandsAndWait(vkd, device, queue, cmdBuffer);
348
349 // Verify output buffer contents.
350 vk::invalidateAlloc(vkd, device, outputAlloc);
351 BufferStructure *outputData = reinterpret_cast<BufferStructure *>(outputBufferPtr);
352 return (outputData->checkValues(kA, kB, kC, kOutputPaddingByte) ?
353 tcu::TestStatus::pass("Pass") :
354 tcu::TestStatus::fail("Unexpected values in output data"));
355 }
356
357 } // namespace
358
createPaddingTests(tcu::TestContext & testCtx)359 tcu::TestCaseGroup *createPaddingTests(tcu::TestContext &testCtx)
360 {
361 // Padding bytes tests
362 de::MovePtr<tcu::TestCaseGroup> paddingGroup(new tcu::TestCaseGroup(testCtx, "padding"));
363 // Check padding bytes at the end of structures are not touched on copy
364 paddingGroup->addChild(new PaddingTest(testCtx, "test"));
365
366 return paddingGroup.release();
367 }
368
369 } // namespace MemoryModel
370 } // namespace vkt
371