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 deInt32 a;
51 deUint8 padding[12];
52 };
53
54 // Structure with an 8-byte padding at the end.
55 struct Pad8
56 {
57 deInt32 a, b;
58 deUint8 padding[8];
59 };
60
61 // Structure with a 4-byte padding at the end.
62 struct Pad4
63 {
64 deInt32 a, b, c;
65 deUint8 padding[4];
66 };
67
68 // Buffer structure for the input and output buffers.
69 struct BufferStructure
70 {
71 static constexpr deUint32 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::__anona953cbd40111::BufferStructure78 BufferStructure (deInt32 a, deInt32 b, deInt32 c, deUint8 paddingByte)
79 {
80 for (deUint32 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::__anona953cbd40111::BufferStructure95 BufferStructure (deUint8 paddingByte)
96 : BufferStructure (0, 0, 0, paddingByte)
97 {}
98
99 // Verify members and padding bytes.
checkValuesvkt::MemoryModel::__anona953cbd40111::BufferStructure100 bool checkValues (deInt32 a, deInt32 b, deInt32 c, deUint8 paddingByte) const
101 {
102 for (deUint32 i = 0; i < kArrayLength; ++i)
103 {
104 if (subA[i].a != a || subB[i].a != a || subC[i].a != a ||
105 subB[i].b != b || subC[i].b != b ||
106 subC[i].c != c)
107 return false;
108 }
109 return checkPaddingBytes(paddingByte);
110 }
111
112 // Verify padding bytes have a known value.
checkPaddingBytesvkt::MemoryModel::__anona953cbd40111::BufferStructure113 bool checkPaddingBytes (deUint8 value) const
114 {
115 for (deUint32 j = 0; j < kArrayLength; ++j)
116 {
117 for (int i = 0; i < DE_LENGTH_OF_ARRAY(subA[j].padding); ++i)
118 {
119 if (subA[j].padding[i] != value)
120 return false;
121 }
122 for (int i = 0; i < DE_LENGTH_OF_ARRAY(subB[j].padding); ++i)
123 {
124 if (subB[j].padding[i] != value)
125 return false;
126 }
127 for (int i = 0; i < DE_LENGTH_OF_ARRAY(subC[j].padding); ++i)
128 {
129 if (subC[j].padding[i] != value)
130 return false;
131 }
132 }
133 return true;
134 }
135 };
136
137 class PaddingTest : public vkt::TestCase
138 {
139 public:
140 PaddingTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description);
~PaddingTest(void)141 virtual ~PaddingTest (void) {}
142
143 virtual void initPrograms (vk::SourceCollections& programCollection) const;
144 virtual TestInstance* createInstance (Context& context) const;
145 virtual void checkSupport (Context& context) const;
146
iterate(void)147 IterateResult iterate (void) { DE_ASSERT(false); return STOP; } // Deprecated in this module
148 };
149
150 class PaddingTestInstance : public vkt::TestInstance
151 {
152 public:
PaddingTestInstance(Context & context)153 PaddingTestInstance (Context& context)
154 : vkt::TestInstance(context)
155 {}
~PaddingTestInstance(void)156 virtual ~PaddingTestInstance (void) {}
157
158 virtual tcu::TestStatus iterate (void);
159 };
160
161
PaddingTest(tcu::TestContext & testCtx,const std::string & name,const std::string & description)162 PaddingTest::PaddingTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description)
163 : vkt::TestCase(testCtx, name, description)
164 {
165 }
166
createInstance(Context & context) const167 TestInstance* PaddingTest::createInstance (Context& context) const
168 {
169 return new PaddingTestInstance(context);
170 }
171
initPrograms(vk::SourceCollections & programCollection) const172 void PaddingTest::initPrograms (vk::SourceCollections& programCollection) const
173 {
174 const std::string arrayLenghtStr = std::to_string(BufferStructure::kArrayLength);
175
176 std::ostringstream shaderSrc;
177 shaderSrc
178 << "#version 450\n"
179 << "#pragma use_vulkan_memory_model\n"
180 << "\n"
181 << "struct A {\n"
182 << " int a;\n"
183 << "};\n"
184 << "\n"
185 << "struct B {\n"
186 << " int a, b;\n"
187 << "};\n"
188 << "\n"
189 << "struct C {\n"
190 << " int a, b, c;\n"
191 << "};\n"
192 << "\n"
193 << "struct BufferStructure {\n"
194 << " A subA[" << arrayLenghtStr << "];\n"
195 << " B subB[" << arrayLenghtStr << "];\n"
196 << " C subC[" << arrayLenghtStr << "];\n"
197 << "};\n"
198 << "\n"
199 << "layout (set=0, binding=0, std140) uniform InputBlock\n"
200 << "{\n"
201 << " BufferStructure inBlock;\n"
202 << "};\n"
203 << "\n"
204 << "layout (set=0, binding=1, std140) buffer OutputBlock\n"
205 << "{\n"
206 << " BufferStructure outBlock;\n"
207 << "};\n"
208 << "\n"
209 << "void main()\n"
210 << "{\n"
211 << " const uint idx = gl_GlobalInvocationID.x;\n"
212 << " outBlock.subA[idx] = inBlock.subA[idx];\n"
213 << " outBlock.subB[idx] = inBlock.subB[idx];\n"
214 << " outBlock.subC[idx] = inBlock.subC[idx];\n"
215 << "}\n";
216
217 programCollection.glslSources.add("comp") << glu::ComputeSource(shaderSrc.str());
218 }
219
checkSupport(Context & context) const220 void PaddingTest::checkSupport (Context& context) const
221 {
222 context.requireDeviceFunctionality("VK_KHR_vulkan_memory_model");
223 if (!context.getVulkanMemoryModelFeatures().vulkanMemoryModel)
224 {
225 TCU_THROW(NotSupportedError, "Vulkan memory model not supported");
226 }
227 }
228
iterate(void)229 tcu::TestStatus PaddingTestInstance::iterate (void)
230 {
231 const auto& vkd = m_context.getDeviceInterface();
232 const auto device = m_context.getDevice();
233 auto& allocator = m_context.getDefaultAllocator();
234 const auto queue = m_context.getUniversalQueue();
235 const auto queueIndex = m_context.getUniversalQueueFamilyIndex();
236
237 constexpr vk::VkDeviceSize kBufferSize = static_cast<vk::VkDeviceSize>(sizeof(BufferStructure));
238 constexpr deInt32 kA = 1;
239 constexpr deInt32 kB = 2;
240 constexpr deInt32 kC = 3;
241 constexpr deUint8 kInputPaddingByte = 0xFEu;
242 constexpr deUint8 kOutputPaddingByte = 0x7Fu;
243
244 // Create input and output buffers.
245 auto inputBufferInfo = vk::makeBufferCreateInfo(kBufferSize, vk::VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT);
246 auto outputBufferInfo = vk::makeBufferCreateInfo(kBufferSize, vk::VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
247
248 vk::BufferWithMemory inputBuffer {vkd, device, allocator, inputBufferInfo, vk::MemoryRequirement::HostVisible};
249 vk::BufferWithMemory outputBuffer {vkd, device, allocator, outputBufferInfo, vk::MemoryRequirement::HostVisible};
250
251 // Fill buffers with initial contents.
252 BufferStructure inputValues {kA, kB, kC, kInputPaddingByte};
253 BufferStructure outputInit {kOutputPaddingByte};
254
255 auto& inputAlloc = inputBuffer.getAllocation();
256 auto& outputAlloc = outputBuffer.getAllocation();
257
258 void* inputBufferPtr = static_cast<deUint8*>(inputAlloc.getHostPtr()) + inputAlloc.getOffset();
259 void* outputBufferPtr = static_cast<deUint8*>(outputAlloc.getHostPtr()) + outputAlloc.getOffset();
260
261 deMemcpy(inputBufferPtr, &inputValues, sizeof(inputValues));
262 deMemcpy(outputBufferPtr, &outputInit, sizeof(outputInit));
263
264 vk::flushAlloc(vkd, device, inputAlloc);
265 vk::flushAlloc(vkd, device, outputAlloc);
266
267 // Descriptor set layout.
268 vk::DescriptorSetLayoutBuilder layoutBuilder;
269 layoutBuilder.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, vk::VK_SHADER_STAGE_COMPUTE_BIT);
270 layoutBuilder.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, vk::VK_SHADER_STAGE_COMPUTE_BIT);
271 auto descriptorSetLayout = layoutBuilder.build(vkd, device);
272
273 // Descriptor pool.
274 vk::DescriptorPoolBuilder poolBuilder;
275 poolBuilder.addType(vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
276 poolBuilder.addType(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
277 auto descriptorPool = poolBuilder.build(vkd, device, vk::VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
278
279 // Descriptor set.
280 const auto descriptorSet = vk::makeDescriptorSet(vkd, device, descriptorPool.get(), descriptorSetLayout.get());
281
282 // Update descriptor set using the buffers.
283 const auto inputBufferDescriptorInfo = vk::makeDescriptorBufferInfo(inputBuffer.get(), 0ull, VK_WHOLE_SIZE);
284 const auto outputBufferDescriptorInfo = vk::makeDescriptorBufferInfo(outputBuffer.get(), 0ull, VK_WHOLE_SIZE);
285
286 vk::DescriptorSetUpdateBuilder updateBuilder;
287 updateBuilder.writeSingle(descriptorSet.get(), vk::DescriptorSetUpdateBuilder::Location::binding(0u), vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &inputBufferDescriptorInfo);
288 updateBuilder.writeSingle(descriptorSet.get(), vk::DescriptorSetUpdateBuilder::Location::binding(1u), vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &outputBufferDescriptorInfo);
289 updateBuilder.update(vkd, device);
290
291 // Create compute pipeline.
292 auto shaderModule = vk::createShaderModule(vkd, device, m_context.getBinaryCollection().get("comp"), 0u);
293 auto pipelineLayout = vk::makePipelineLayout(vkd, device, descriptorSetLayout.get());
294
295 const vk::VkComputePipelineCreateInfo pipelineCreateInfo =
296 {
297 vk::VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
298 nullptr,
299 0u, // flags
300 { // compute shader
301 vk::VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // VkStructureType sType;
302 nullptr, // const void* pNext;
303 0u, // VkPipelineShaderStageCreateFlags flags;
304 vk::VK_SHADER_STAGE_COMPUTE_BIT, // VkShaderStageFlagBits stage;
305 shaderModule.get(), // VkShaderModule module;
306 "main", // const char* pName;
307 nullptr, // const VkSpecializationInfo* pSpecializationInfo;
308 },
309 pipelineLayout.get(), // layout
310 DE_NULL, // basePipelineHandle
311 0, // basePipelineIndex
312 };
313 auto pipeline = vk::createComputePipeline(vkd, device, DE_NULL, &pipelineCreateInfo);
314
315 // Synchronization barriers.
316 auto inputBufferHostToDevBarrier = vk::makeBufferMemoryBarrier(vk::VK_ACCESS_HOST_WRITE_BIT, vk::VK_ACCESS_SHADER_READ_BIT, inputBuffer.get(), 0ull, VK_WHOLE_SIZE);
317 auto outputBufferHostToDevBarrier = vk::makeBufferMemoryBarrier(vk::VK_ACCESS_HOST_WRITE_BIT, vk::VK_ACCESS_SHADER_WRITE_BIT, outputBuffer.get(), 0ull, VK_WHOLE_SIZE);
318 auto outputBufferDevToHostBarrier = vk::makeBufferMemoryBarrier(vk::VK_ACCESS_SHADER_WRITE_BIT, vk::VK_ACCESS_HOST_READ_BIT, outputBuffer.get(), 0ull, VK_WHOLE_SIZE);
319
320 // Command buffer.
321 auto cmdPool = vk::makeCommandPool(vkd, device, queueIndex);
322 auto cmdBufferPtr = vk::allocateCommandBuffer(vkd, device, cmdPool.get(), vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY);
323 auto cmdBuffer = cmdBufferPtr.get();
324
325 // Record and submit commands.
326 vk::beginCommandBuffer(vkd, cmdBuffer);
327 vkd.cmdBindPipeline(cmdBuffer, vk::VK_PIPELINE_BIND_POINT_COMPUTE, pipeline.get());
328 vkd.cmdBindDescriptorSets(cmdBuffer, vk::VK_PIPELINE_BIND_POINT_COMPUTE, pipelineLayout.get(), 0, 1u, &descriptorSet.get(), 0u, nullptr);
329 vkd.cmdPipelineBarrier(cmdBuffer, vk::VK_PIPELINE_STAGE_HOST_BIT, vk::VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0u, 0u, nullptr, 1u, &inputBufferHostToDevBarrier, 0u, nullptr);
330 vkd.cmdPipelineBarrier(cmdBuffer, vk::VK_PIPELINE_STAGE_HOST_BIT, vk::VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0u, 0u, nullptr, 1u, &outputBufferHostToDevBarrier, 0u, nullptr);
331 vkd.cmdDispatch(cmdBuffer, BufferStructure::kArrayLength, 1u, 1u);
332 vkd.cmdPipelineBarrier(cmdBuffer, vk::VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, vk::VK_PIPELINE_STAGE_HOST_BIT, 0u, 0u, nullptr, 1u, &outputBufferDevToHostBarrier, 0u, nullptr);
333 vk::endCommandBuffer(vkd, cmdBuffer);
334 vk::submitCommandsAndWait(vkd, device, queue, cmdBuffer);
335
336 // Verify output buffer contents.
337 vk::invalidateAlloc(vkd, device, outputAlloc);
338 BufferStructure* outputData = reinterpret_cast<BufferStructure*>(outputBufferPtr);
339 return (outputData->checkValues(kA, kB, kC, kOutputPaddingByte) ? tcu::TestStatus::pass("Pass") : tcu::TestStatus::fail("Unexpected values in output data"));
340 }
341
342 } // anonymous
343
createPaddingTests(tcu::TestContext & testCtx)344 tcu::TestCaseGroup* createPaddingTests (tcu::TestContext& testCtx)
345 {
346 de::MovePtr<tcu::TestCaseGroup> paddingGroup(new tcu::TestCaseGroup(testCtx, "padding", "Padding bytes tests"));
347 paddingGroup->addChild(new PaddingTest(testCtx, "test", "Check padding bytes at the end of structures are not touched on copy"));
348
349 return paddingGroup.release();
350 }
351
352 } // MemoryModel
353 } // vkt
354