xref: /aosp_15_r20/external/deqp/external/vulkancts/modules/vulkan/memory_model/vktMemoryModelPadding.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
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