--- /dev/null
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2019 The Khronos Group Inc.
+ * Copyright (c) 2019 Valve Corporation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Vulkan Memory Model padding access tests
+ *//*--------------------------------------------------------------------*/
+
+#include "vktMemoryModelPadding.hpp"
+#include "vktTestCase.hpp"
+
+#include "vkBufferWithMemory.hpp"
+#include "vkBarrierUtil.hpp"
+#include "vkObjUtil.hpp"
+#include "vkBuilderUtil.hpp"
+#include "vkTypeUtil.hpp"
+#include "vkCmdUtil.hpp"
+
+#include "deMemory.h"
+
+namespace vkt
+{
+namespace MemoryModel
+{
+
+namespace
+{
+// The structures below match the shader declarations but have explicit padding members at the end so we can check their contents
+// easily after running the shader. Using the std140 layout means structures are aligned to 16 bytes.
+
+// Structure with a 12-byte padding at the end.
+struct Pad12
+{
+ deInt32 a;
+ deUint8 padding[12];
+};
+
+// Structure with an 8-byte padding at the end.
+struct Pad8
+{
+ deInt32 a, b;
+ deUint8 padding[8];
+};
+
+// Structure with a 4-byte padding at the end.
+struct Pad4
+{
+ deInt32 a, b, c;
+ deUint8 padding[4];
+};
+
+// Buffer structure for the input and output buffers.
+struct BufferStructure
+{
+ static constexpr deUint32 kArrayLength = 3u;
+
+ Pad12 subA[kArrayLength];
+ Pad8 subB[kArrayLength];
+ Pad4 subC[kArrayLength];
+
+ // Pre-fill substructures with the given data.
+ BufferStructure (deInt32 a, deInt32 b, deInt32 c, deUint8 paddingByte)
+ {
+ for (deUint32 i = 0; i < kArrayLength; ++i)
+ {
+ subA[i].a = a;
+ subB[i].a = a;
+ subC[i].a = a;
+ subB[i].b = b;
+ subC[i].b = b;
+ subC[i].c = c;
+ deMemset(subA[i].padding, static_cast<int>(paddingByte), sizeof(subA[i].padding));
+ deMemset(subB[i].padding, static_cast<int>(paddingByte), sizeof(subB[i].padding));
+ deMemset(subC[i].padding, static_cast<int>(paddingByte), sizeof(subC[i].padding));
+ }
+ }
+
+ // Pre-fill substructures with zeros.
+ BufferStructure (deUint8 paddingByte)
+ : BufferStructure (0, 0, 0, paddingByte)
+ {}
+
+ // Verify members and padding bytes.
+ bool checkValues (deInt32 a, deInt32 b, deInt32 c, deUint8 paddingByte) const
+ {
+ for (deUint32 i = 0; i < kArrayLength; ++i)
+ {
+ if (subA[i].a != a || subB[i].a != a || subC[i].a != a ||
+ subB[i].b != b || subC[i].b != b ||
+ subC[i].c != c)
+ return false;
+ }
+ return checkPaddingBytes(paddingByte);
+ }
+
+ // Verify padding bytes have a known value.
+ bool checkPaddingBytes (deUint8 value) const
+ {
+ for (deUint32 j = 0; j < kArrayLength; ++j)
+ {
+ for (int i = 0; i < DE_LENGTH_OF_ARRAY(subA[j].padding); ++i)
+ {
+ if (subA[j].padding[i] != value)
+ return false;
+ }
+ for (int i = 0; i < DE_LENGTH_OF_ARRAY(subB[j].padding); ++i)
+ {
+ if (subB[j].padding[i] != value)
+ return false;
+ }
+ for (int i = 0; i < DE_LENGTH_OF_ARRAY(subC[j].padding); ++i)
+ {
+ if (subC[j].padding[i] != value)
+ return false;
+ }
+ }
+ return true;
+ }
+};
+
+class PaddingTest : public vkt::TestCase
+{
+public:
+ PaddingTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description);
+ virtual ~PaddingTest (void) {}
+
+ virtual void initPrograms (vk::SourceCollections& programCollection) const;
+ virtual TestInstance* createInstance (Context& context) const;
+ virtual void checkSupport (Context& context) const;
+
+ IterateResult iterate (void) { DE_ASSERT(false); return STOP; } // Deprecated in this module
+};
+
+class PaddingTestInstance : public vkt::TestInstance
+{
+public:
+ PaddingTestInstance (Context& context)
+ : vkt::TestInstance(context)
+ {}
+ virtual ~PaddingTestInstance (void) {}
+
+ virtual tcu::TestStatus iterate (void);
+};
+
+
+PaddingTest::PaddingTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description)
+ : vkt::TestCase(testCtx, name, description)
+{
+}
+
+TestInstance* PaddingTest::createInstance (Context& context) const
+{
+ return new PaddingTestInstance(context);
+}
+
+void PaddingTest::initPrograms (vk::SourceCollections& programCollection) const
+{
+ const std::string arrayLenghtStr = std::to_string(BufferStructure::kArrayLength);
+
+ std::ostringstream shaderSrc;
+ shaderSrc
+ << "#version 450\n"
+ << "#pragma use_vulkan_memory_model\n"
+ << "\n"
+ << "struct A {\n"
+ << " int a;\n"
+ << "};\n"
+ << "\n"
+ << "struct B {\n"
+ << " int a, b;\n"
+ << "};\n"
+ << "\n"
+ << "struct C {\n"
+ << " int a, b, c;\n"
+ << "};\n"
+ << "\n"
+ << "struct BufferStructure {\n"
+ << " A subA[" << arrayLenghtStr << "];\n"
+ << " B subB[" << arrayLenghtStr << "];\n"
+ << " C subC[" << arrayLenghtStr << "];\n"
+ << "};\n"
+ << "\n"
+ << "layout (set=0, binding=0, std140) uniform InputBlock\n"
+ << "{\n"
+ << " BufferStructure inBlock;\n"
+ << "};\n"
+ << "\n"
+ << "layout (set=0, binding=1, std140) buffer OutputBlock\n"
+ << "{\n"
+ << " BufferStructure outBlock;\n"
+ << "};\n"
+ << "\n"
+ << "void main()\n"
+ << "{\n"
+ << " const uint idx = gl_GlobalInvocationID.x;\n"
+ << " outBlock.subA[idx] = inBlock.subA[idx];\n"
+ << " outBlock.subB[idx] = inBlock.subB[idx];\n"
+ << " outBlock.subC[idx] = inBlock.subC[idx];\n"
+ << "}\n";
+
+ programCollection.glslSources.add("comp") << glu::ComputeSource(shaderSrc.str());
+}
+
+void PaddingTest::checkSupport (Context& context) const
+{
+ context.requireDeviceFunctionality("VK_KHR_vulkan_memory_model");
+ if (!context.getVulkanMemoryModelFeatures().vulkanMemoryModel)
+ {
+ TCU_THROW(NotSupportedError, "Vulkan memory model not supported");
+ }
+}
+
+tcu::TestStatus PaddingTestInstance::iterate (void)
+{
+ const auto& vkd = m_context.getDeviceInterface();
+ const auto device = m_context.getDevice();
+ auto& allocator = m_context.getDefaultAllocator();
+ const auto queue = m_context.getUniversalQueue();
+ const auto queueIndex = m_context.getUniversalQueueFamilyIndex();
+
+ constexpr vk::VkDeviceSize kBufferSize = static_cast<vk::VkDeviceSize>(sizeof(BufferStructure));
+ constexpr deInt32 kA = 1;
+ constexpr deInt32 kB = 2;
+ constexpr deInt32 kC = 3;
+ constexpr deUint8 kInputPaddingByte = 0xFEu;
+ constexpr deUint8 kOutputPaddingByte = 0x7Fu;
+
+ // Create input and output buffers.
+ auto inputBufferInfo = vk::makeBufferCreateInfo(kBufferSize, vk::VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT);
+ auto outputBufferInfo = vk::makeBufferCreateInfo(kBufferSize, vk::VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
+
+ vk::BufferWithMemory inputBuffer {vkd, device, allocator, inputBufferInfo, vk::MemoryRequirement::HostVisible};
+ vk::BufferWithMemory outputBuffer {vkd, device, allocator, outputBufferInfo, vk::MemoryRequirement::HostVisible};
+
+ // Fill buffers with initial contents.
+ BufferStructure inputValues {kA, kB, kC, kInputPaddingByte};
+ BufferStructure outputInit {kOutputPaddingByte};
+
+ auto& inputAlloc = inputBuffer.getAllocation();
+ auto& outputAlloc = outputBuffer.getAllocation();
+
+ void* inputBufferPtr = static_cast<deUint8*>(inputAlloc.getHostPtr()) + inputAlloc.getOffset();
+ void* outputBufferPtr = static_cast<deUint8*>(outputAlloc.getHostPtr()) + outputAlloc.getOffset();
+
+ deMemcpy(inputBufferPtr, &inputValues, sizeof(inputValues));
+ deMemcpy(outputBufferPtr, &outputInit, sizeof(outputInit));
+
+ vk::flushAlloc(vkd, device, inputAlloc);
+ vk::flushAlloc(vkd, device, outputAlloc);
+
+ // Descriptor set layout.
+ vk::DescriptorSetLayoutBuilder layoutBuilder;
+ layoutBuilder.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, vk::VK_SHADER_STAGE_COMPUTE_BIT);
+ layoutBuilder.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, vk::VK_SHADER_STAGE_COMPUTE_BIT);
+ auto descriptorSetLayout = layoutBuilder.build(vkd, device);
+
+ // Descriptor pool.
+ vk::DescriptorPoolBuilder poolBuilder;
+ poolBuilder.addType(vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
+ poolBuilder.addType(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
+ auto descriptorPool = poolBuilder.build(vkd, device, vk::VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
+
+ // Descriptor set.
+ const auto descriptorSet = vk::makeDescriptorSet(vkd, device, descriptorPool.get(), descriptorSetLayout.get());
+
+ // Update descriptor set using the buffers.
+ const auto inputBufferDescriptorInfo = vk::makeDescriptorBufferInfo(inputBuffer.get(), 0ull, VK_WHOLE_SIZE);
+ const auto outputBufferDescriptorInfo = vk::makeDescriptorBufferInfo(outputBuffer.get(), 0ull, VK_WHOLE_SIZE);
+
+ vk::DescriptorSetUpdateBuilder updateBuilder;
+ updateBuilder.writeSingle(descriptorSet.get(), vk::DescriptorSetUpdateBuilder::Location::binding(0u), vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &inputBufferDescriptorInfo);
+ updateBuilder.writeSingle(descriptorSet.get(), vk::DescriptorSetUpdateBuilder::Location::binding(1u), vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &outputBufferDescriptorInfo);
+ updateBuilder.update(vkd, device);
+
+ // Create compute pipeline.
+ auto shaderModule = vk::createShaderModule(vkd, device, m_context.getBinaryCollection().get("comp"), 0u);
+ auto pipelineLayout = vk::makePipelineLayout(vkd, device, descriptorSetLayout.get());
+
+ const vk::VkComputePipelineCreateInfo pipelineCreateInfo =
+ {
+ vk::VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
+ nullptr,
+ 0u, // flags
+ { // compute shader
+ vk::VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // VkStructureType sType;
+ nullptr, // const void* pNext;
+ 0u, // VkPipelineShaderStageCreateFlags flags;
+ vk::VK_SHADER_STAGE_COMPUTE_BIT, // VkShaderStageFlagBits stage;
+ shaderModule.get(), // VkShaderModule module;
+ "main", // const char* pName;
+ nullptr, // const VkSpecializationInfo* pSpecializationInfo;
+ },
+ pipelineLayout.get(), // layout
+ DE_NULL, // basePipelineHandle
+ 0, // basePipelineIndex
+ };
+ auto pipeline = vk::createComputePipeline(vkd, device, DE_NULL, &pipelineCreateInfo);
+
+ // Synchronization barriers.
+ auto inputBufferHostToDevBarrier = vk::makeBufferMemoryBarrier(vk::VK_ACCESS_HOST_WRITE_BIT, vk::VK_ACCESS_SHADER_READ_BIT, inputBuffer.get(), 0ull, VK_WHOLE_SIZE);
+ auto outputBufferHostToDevBarrier = vk::makeBufferMemoryBarrier(vk::VK_ACCESS_HOST_WRITE_BIT, vk::VK_ACCESS_SHADER_WRITE_BIT, outputBuffer.get(), 0ull, VK_WHOLE_SIZE);
+ auto outputBufferDevToHostBarrier = vk::makeBufferMemoryBarrier(vk::VK_ACCESS_SHADER_WRITE_BIT, vk::VK_ACCESS_HOST_READ_BIT, outputBuffer.get(), 0ull, VK_WHOLE_SIZE);
+
+ // Command buffer.
+ auto cmdPool = vk::makeCommandPool(vkd, device, queueIndex);
+ auto cmdBufferPtr = vk::allocateCommandBuffer(vkd, device, cmdPool.get(), vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY);
+ auto cmdBuffer = cmdBufferPtr.get();
+
+ // Record and submit commands.
+ vk::beginCommandBuffer(vkd, cmdBuffer);
+ vkd.cmdBindPipeline(cmdBuffer, vk::VK_PIPELINE_BIND_POINT_COMPUTE, pipeline.get());
+ vkd.cmdBindDescriptorSets(cmdBuffer, vk::VK_PIPELINE_BIND_POINT_COMPUTE, pipelineLayout.get(), 0, 1u, &descriptorSet.get(), 0u, nullptr);
+ vkd.cmdPipelineBarrier(cmdBuffer, vk::VK_PIPELINE_STAGE_HOST_BIT, vk::VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0u, 0u, nullptr, 1u, &inputBufferHostToDevBarrier, 0u, nullptr);
+ vkd.cmdPipelineBarrier(cmdBuffer, vk::VK_PIPELINE_STAGE_HOST_BIT, vk::VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0u, 0u, nullptr, 1u, &outputBufferHostToDevBarrier, 0u, nullptr);
+ vkd.cmdDispatch(cmdBuffer, BufferStructure::kArrayLength, 1u, 1u);
+ vkd.cmdPipelineBarrier(cmdBuffer, vk::VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, vk::VK_PIPELINE_STAGE_HOST_BIT, 0u, 0u, nullptr, 1u, &outputBufferDevToHostBarrier, 0u, nullptr);
+ vk::endCommandBuffer(vkd, cmdBuffer);
+ vk::submitCommandsAndWait(vkd, device, queue, cmdBuffer);
+
+ // Verify output buffer contents.
+ vk::invalidateAlloc(vkd, device, outputAlloc);
+ BufferStructure* outputData = reinterpret_cast<BufferStructure*>(outputBufferPtr);
+ return (outputData->checkValues(kA, kB, kC, kOutputPaddingByte) ? tcu::TestStatus::pass("Pass") : tcu::TestStatus::fail("Unexpected values in output data"));
+}
+
+} // anonymous
+
+tcu::TestCaseGroup* createPaddingTests (tcu::TestContext& testCtx)
+{
+ de::MovePtr<tcu::TestCaseGroup> paddingGroup(new tcu::TestCaseGroup(testCtx, "padding", "Padding bytes tests"));
+ paddingGroup->addChild(new PaddingTest(testCtx, "test", "Check padding bytes at the end of structures are not touched on copy"));
+
+ return paddingGroup.release();
+}
+
+} // MemoryModel
+} // vkt