blob: 6c02f3aa759489e89602292e179aec6f79dfd679 [file] [log] [blame]
/*------------------------------------------------------------------------
* 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