blob: 13aa18bfb8104f90dcee60924f3222616aba5f18 [file] [log] [blame]
/*-------------------------------------------------------------------------
* Vulkan Conformance Tests
* ------------------------
*
* Copyright (c) 2018 Google Inc.
*
* 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 Tests for descriptor updates.
*//*--------------------------------------------------------------------*/
#include "vktBindingDescriptorUpdateTests.hpp"
#ifndef CTS_USES_VULKANSC
#include "vktBindingDescriptorUpdateASTests.hpp"
#endif // CTS_USES_VULKANSC
#include "vktTestCase.hpp"
#include "vktTestCaseUtil.hpp"
#include "vkRefUtil.hpp"
#include "vkMemUtil.hpp"
#include "vkBuilderUtil.hpp"
#include "vkQueryUtil.hpp"
#include "vkTypeUtil.hpp"
#include "vkObjUtil.hpp"
#include "vkCmdUtil.hpp"
#include "vkImageUtil.hpp"
#include "vkBarrierUtil.hpp"
#include "vkImageWithMemory.hpp"
#include "vkBufferWithMemory.hpp"
#include "tcuTexture.hpp"
#include "tcuTestLog.hpp"
#include "deRandom.hpp"
#include <string>
#include <vector>
#include <utility>
#include <memory>
#include <math.h>
namespace vkt
{
namespace BindingModel
{
namespace
{
// Test matches VkPositiveLayerTest.EmptyDescriptorUpdateTest
tcu::TestStatus EmptyDescriptorUpdateCase (Context& context)
{
const vk::DeviceInterface& vki = context.getDeviceInterface();
const vk::VkDevice device = context.getDevice();
vk::Allocator& allocator = context.getDefaultAllocator();
// Create layout with two uniform buffer descriptors w/ empty binding between them
vk::DescriptorSetLayoutBuilder builder;
builder.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, vk::VK_SHADER_STAGE_ALL);
builder.addBinding(vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, (vk::VkShaderStageFlags)0, DE_NULL);
builder.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, vk::VK_SHADER_STAGE_ALL);
vk::Unique<vk::VkDescriptorSetLayout> layout (builder.build(vki, device, (vk::VkDescriptorSetLayoutCreateFlags)0));
// Create descriptor pool
vk::Unique<vk::VkDescriptorPool> descriptorPool (vk::DescriptorPoolBuilder().addType(vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2).build(vki, device, vk::VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1));
// Create descriptor set
const vk::VkDescriptorSetAllocateInfo setAllocateInfo =
{
vk::VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, // VkStructureType sType
DE_NULL, // const void* pNext
*descriptorPool, // VkDescriptorPool descriptorPool
1, // deUint32 descriptorSetCount
&layout.get() // const VkDescriptorSetLayout* pSetLayouts
};
vk::Unique<vk::VkDescriptorSet> descriptorSet (allocateDescriptorSet(vki, device, &setAllocateInfo));
// Create a buffer to be used for update
const vk::VkBufferCreateInfo bufferCreateInfo =
{
vk::VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType
DE_NULL, // const void* pNext
(vk::VkBufferCreateFlags)DE_NULL, // VkBufferCreateFlags flags
256, // VkDeviceSize size
vk::VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, // VkBufferUsageFlags usage
vk::VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode
0, // deUint32 queueFamilyIndexCount
DE_NULL // const deUint32* pQueueFamilyIndices
};
vk::Unique<vk::VkBuffer> buffer (createBuffer(vki, device, &bufferCreateInfo));
const vk::VkMemoryRequirements requirements = vk::getBufferMemoryRequirements(vki, device, *buffer);
de::MovePtr<vk::Allocation> allocation = allocator.allocate(requirements, vk::MemoryRequirement::Any);
VK_CHECK(vki.bindBufferMemory(device, *buffer, allocation->getMemory(), allocation->getOffset()));
// Only update the descriptor at binding 2
const vk::VkDescriptorBufferInfo descriptorInfo =
{
*buffer, // VkBuffer buffer
0, // VkDeviceSize offset
VK_WHOLE_SIZE // VkDeviceSize range
};
const vk::VkWriteDescriptorSet descriptorWrite =
{
vk::VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, // VkStructureTypes Type
DE_NULL, // const void* pNext
*descriptorSet, // VkDescriptorSet dstSet
2, // deUint32 dstBinding
0, // deUint32 dstArrayElement
1, // deUint32 descriptorCount
vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, // VkDescriptorType descriptorType
DE_NULL, // const VkDescriptorImageInfo* pImageInfo
&descriptorInfo, // const VkDescriptorBufferInfo* pBufferInfo
DE_NULL // const VkBufferView* pTexelBufferView
};
vki.updateDescriptorSets(device, 1, &descriptorWrite, 0, DE_NULL);
// Test should always pass
return tcu::TestStatus::pass("Pass");
}
tcu::TestCaseGroup* createEmptyDescriptorUpdateTests (tcu::TestContext& testCtx)
{
de::MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "empty_descriptor", "Update last descriptor in a set that includes an empty binding"));
addFunctionCase(group.get(), "uniform_buffer", "", EmptyDescriptorUpdateCase);
return group.release();
}
enum class PointerCase
{
ZERO = 0,
ONE,
DESTROYED,
};
struct SamplerlessParams
{
vk::VkDescriptorType type;
PointerCase pointer;
deUint32 descriptorSet;
};
class SamplerlessDescriptorWriteTestCase : public vkt::TestCase
{
public:
SamplerlessDescriptorWriteTestCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const SamplerlessParams& params);
virtual ~SamplerlessDescriptorWriteTestCase (void) {}
virtual void initPrograms (vk::SourceCollections& programCollection) const;
virtual vkt::TestInstance* createInstance (Context& context) const;
virtual void checkSupport (Context& context) const;
vk::VkFormatFeatureFlagBits getMainImageFeature (void) const;
static const vk::VkFormat kImageFormat = vk::VK_FORMAT_R8G8B8A8_UNORM;
private:
SamplerlessParams m_params;
};
class SamplerlessDescriptorWriteTestInstance : public vkt::TestInstance
{
public:
SamplerlessDescriptorWriteTestInstance (Context& context, const SamplerlessParams& params);
virtual ~SamplerlessDescriptorWriteTestInstance (void) {}
vk::VkSampler getSamplerHandle (void) const;
virtual tcu::TestStatus iterate (void);
vk::VkExtent3D getMainImageExtent (void) const;
vk::VkImageUsageFlags getMainImageUsage (void) const;
vk::VkImageLayout getMainImageShaderLayout (void) const;
static const vk::VkFormat kImageFormat = SamplerlessDescriptorWriteTestCase::kImageFormat;
static const vk::VkExtent3D kFramebufferExtent;
static const vk::VkExtent3D kMinimumExtent;
static const tcu::Vec4 kDescriptorColor;
private:
SamplerlessParams m_params;
};
const vk::VkExtent3D SamplerlessDescriptorWriteTestInstance::kFramebufferExtent = vk::makeExtent3D(64u, 64u, 1u);
const vk::VkExtent3D SamplerlessDescriptorWriteTestInstance::kMinimumExtent = vk::makeExtent3D(1u, 1u, 1u);
const tcu::Vec4 SamplerlessDescriptorWriteTestInstance::kDescriptorColor {0.0f, 1.0f, 0.0f, 1.0f};
SamplerlessDescriptorWriteTestCase::SamplerlessDescriptorWriteTestCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const SamplerlessParams& params)
: vkt::TestCase{testCtx, name, description}
, m_params(params)
{
}
void SamplerlessDescriptorWriteTestCase::initPrograms (vk::SourceCollections& programCollection) const
{
const std::string vertexShader =
"#version 450\n"
"layout(location=0) in vec4 position;\n"
"void main() { gl_Position = position; }\n";
programCollection.glslSources.add("vert") << glu::VertexSource(vertexShader);
std::string descriptorDecl;
std::string readOp;
std::string extensions;
switch (m_params.type)
{
case vk::VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
extensions = "#extension GL_EXT_samplerless_texture_functions : require\n";
descriptorDecl = "layout(set=" + std::to_string(m_params.descriptorSet) + ", binding=0) uniform texture2D img;";
readOp = "texelFetch(img, ivec2(0, 0), 0)";
break;
case vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
descriptorDecl = "layout(rgba8, set=" + std::to_string(m_params.descriptorSet) + ", binding=0) uniform image2D img;";
readOp = "imageLoad(img, ivec2(0, 0))";
break;
case vk::VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
descriptorDecl = "layout(input_attachment_index=0, set=" + std::to_string(m_params.descriptorSet) + ", binding=0) uniform subpassInput img;";
readOp = "subpassLoad(img)";
break;
default:
DE_ASSERT(false);
break;
}
std::ostringstream fragmentShader;
fragmentShader
<< "#version 450\n"
<< extensions
<< descriptorDecl << "\n"
<< "layout(location = 0) out vec4 color_out;\n"
<< "void main()\n"
<< "{\n"
<< " color_out = " << readOp << ";\n"
<< "}\n"
;
programCollection.glslSources.add("frag") << glu::FragmentSource(fragmentShader.str());
}
vk::VkFormatFeatureFlagBits SamplerlessDescriptorWriteTestCase::getMainImageFeature (void) const
{
vk::VkFormatFeatureFlagBits feature = static_cast<vk::VkFormatFeatureFlagBits>(0);
switch (m_params.type)
{
case vk::VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: feature = vk::VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT; break;
case vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: feature = vk::VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT; break;
case vk::VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: feature = vk::VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT; break;
default:
DE_ASSERT(false);
break;
}
return feature;
}
void SamplerlessDescriptorWriteTestCase::checkSupport (Context& context) const
{
const auto& vki = context.getInstanceInterface();
const auto physicalDevice = context.getPhysicalDevice();
const auto mainFeature = getMainImageFeature();
const vk::VkFormatFeatureFlags features =
(
vk::VK_FORMAT_FEATURE_TRANSFER_DST_BIT | // For color clearing.
vk::VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | // For the separate frame buffer image (uses the same format).
mainFeature
);
const auto props = vk::getPhysicalDeviceFormatProperties(vki, physicalDevice, kImageFormat);
if ((props.optimalTilingFeatures & features) != features)
TCU_THROW(NotSupportedError, "Image format does not support the required features");
}
vkt::TestInstance* SamplerlessDescriptorWriteTestCase::createInstance (Context& context) const
{
return new SamplerlessDescriptorWriteTestInstance{context, m_params};
}
SamplerlessDescriptorWriteTestInstance::SamplerlessDescriptorWriteTestInstance (Context& context, const SamplerlessParams& params)
: vkt::TestInstance{context}
, m_params(params)
{
}
struct DestroyedSampler
{
vk::VkSampler sampler;
DestroyedSampler (Context& context)
: sampler{DE_NULL}
{
const vk::VkSamplerCreateInfo createInfo =
{
vk::VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, // VkStructureType sType;
nullptr, // const void* pNext;
0u, // VkSamplerCreateFlags flags;
vk::VK_FILTER_NEAREST, // VkFilter magFilter;
vk::VK_FILTER_NEAREST, // VkFilter minFilter;
vk::VK_SAMPLER_MIPMAP_MODE_NEAREST, // VkSamplerMipmapMode mipmapMode;
vk::VK_SAMPLER_ADDRESS_MODE_REPEAT, // VkSamplerAddressMode addressModeU;
vk::VK_SAMPLER_ADDRESS_MODE_REPEAT, // VkSamplerAddressMode addressModeV;
vk::VK_SAMPLER_ADDRESS_MODE_REPEAT, // VkSamplerAddressMode addressModeW;
0.0f, // float mipLodBias;
VK_FALSE, // VkBool32 anisotropyEnable;
1.0f, // float maxAnisotropy;
VK_FALSE, // VkBool32 compareEnable;
vk::VK_COMPARE_OP_NEVER, // VkCompareOp compareOp;
0.0f, // float minLod;
0.0f, // float maxLod;
vk::VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, // VkBorderColor borderColor;
VK_FALSE, // VkBool32 unnormalizedCoordinates;
};
const auto newSampler = vk::createSampler(context.getDeviceInterface(), context.getDevice(), &createInfo);
sampler = newSampler.get();
// newSampler will be destroyed here and sampler will hold the former handle.
}
};
vk::VkSampler SamplerlessDescriptorWriteTestInstance::getSamplerHandle (void) const
{
if (m_params.pointer == PointerCase::ZERO) return vk::VkSampler{DE_NULL};
if (m_params.pointer == PointerCase::ONE) return vk::VkSampler{1};
static const DestroyedSampler destroyedSampler{m_context};
return destroyedSampler.sampler;
}
vk::VkExtent3D SamplerlessDescriptorWriteTestInstance::getMainImageExtent (void) const
{
const vk::VkExtent3D* extent = nullptr;
switch (m_params.type)
{
case vk::VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: // fallthrough
case vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: extent = &kMinimumExtent; break;
case vk::VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: extent = &kFramebufferExtent; break;
default:
DE_ASSERT(false);
break;
}
return *extent;
}
vk::VkImageUsageFlags SamplerlessDescriptorWriteTestInstance::getMainImageUsage (void) const
{
vk::VkImageUsageFlags usage = vk::VK_IMAGE_USAGE_TRANSFER_DST_BIT; // Used when clearing the image.
switch (m_params.type)
{
case vk::VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: usage |= vk::VK_IMAGE_USAGE_SAMPLED_BIT; break;
case vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: usage |= vk::VK_IMAGE_USAGE_STORAGE_BIT; break;
case vk::VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: usage |= vk::VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT; break;
default:
DE_ASSERT(false);
break;
}
return usage;
}
vk::VkImageLayout SamplerlessDescriptorWriteTestInstance::getMainImageShaderLayout (void) const
{
vk::VkImageLayout layout = vk::VK_IMAGE_LAYOUT_UNDEFINED;
switch (m_params.type)
{
case vk::VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: // fallthrough
case vk::VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: layout = vk::VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; break;
case vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: layout = vk::VK_IMAGE_LAYOUT_GENERAL; break;
default:
DE_ASSERT(false);
break;
}
return layout;
}
tcu::TestStatus SamplerlessDescriptorWriteTestInstance::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();
const auto tcuFormat = vk::mapVkFormat(kImageFormat);
const vk::VkImageCreateInfo mainImgCreateInfo =
{
vk::VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
nullptr, // const void* pNext;
0u, // VkImageCreateFlags flags;
vk::VK_IMAGE_TYPE_2D, // VkImageType imageType;
kImageFormat, // VkFormat format;
getMainImageExtent(), // VkExtent3D extent;
1u, // deUint32 mipLevels;
1u, // deUint32 arrayLayers;
vk::VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
vk::VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
getMainImageUsage(), // VkImageUsageFlags usage;
vk::VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
1u, // deUint32 queueFamilyIndexCount;
&queueIndex, // const deUint32* pQueueFamilyIndices;
vk::VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
};
const vk::VkImageCreateInfo fbImgCreateInfo =
{
vk::VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
nullptr, // const void* pNext;
0u, // VkImageCreateFlags flags;
vk::VK_IMAGE_TYPE_2D, // VkImageType imageType;
kImageFormat, // VkFormat format;
kFramebufferExtent, // VkExtent3D extent;
1u, // deUint32 mipLevels;
1u, // deUint32 arrayLayers;
vk::VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
vk::VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
(vk::VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | // VkImageUsageFlags usage;
vk::VK_IMAGE_USAGE_TRANSFER_SRC_BIT), // Used when verifying the image.
vk::VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
1u, // deUint32 queueFamilyIndexCount;
&queueIndex, // const deUint32* pQueueFamilyIndices;
vk::VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
};
// Create main and framebuffer images.
const vk::ImageWithMemory mainImage {vkd, device, allocator, mainImgCreateInfo, vk::MemoryRequirement::Any};
const vk::ImageWithMemory fbImage {vkd, device, allocator, fbImgCreateInfo, vk::MemoryRequirement::Any};
// Corresponding image views.
const auto colorSubresourceRange = vk::makeImageSubresourceRange(vk::VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
const auto mainView = vk::makeImageView(vkd, device, mainImage.get(), vk::VK_IMAGE_VIEW_TYPE_2D, kImageFormat, colorSubresourceRange);
const auto fbView = vk::makeImageView(vkd, device, fbImage.get(), vk::VK_IMAGE_VIEW_TYPE_2D, kImageFormat, colorSubresourceRange);
// Buffer to copy rendering result to.
const vk::VkDeviceSize resultsBufferSize = static_cast<vk::VkDeviceSize>(static_cast<deUint32>(tcu::getPixelSize(tcuFormat)) * kFramebufferExtent.width * kFramebufferExtent.height * kFramebufferExtent.depth);
const auto resultsBufferInfo = vk::makeBufferCreateInfo(resultsBufferSize, vk::VK_BUFFER_USAGE_TRANSFER_DST_BIT);
const vk::BufferWithMemory resultsBuffer {vkd, device, allocator, resultsBufferInfo, vk::MemoryRequirement::HostVisible};
const std::vector<tcu::Vec4> fullScreenQuad =
{
{ -1.f, -1.f, 0.f, 1.f },
{ 1.f, -1.f, 0.f, 1.f },
{ -1.f, 1.f, 0.f, 1.f },
{ -1.f, 1.f, 0.f, 1.f },
{ 1.f, -1.f, 0.f, 1.f },
{ 1.f, 1.f, 0.f, 1.f },
};
// Vertex buffer.
const vk::VkDeviceSize vertexBufferSize = static_cast<vk::VkDeviceSize>(fullScreenQuad.size() * sizeof(decltype(fullScreenQuad)::value_type));
const auto vertexBufferInfo = vk::makeBufferCreateInfo(vertexBufferSize, vk::VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
const vk::BufferWithMemory vertexBuffer {vkd, device, allocator, vertexBufferInfo, vk::MemoryRequirement::HostVisible};
// Copy data to vertex buffer.
const auto& vertexAlloc = vertexBuffer.getAllocation();
const auto vertexDataPtr = reinterpret_cast<char*>(vertexAlloc.getHostPtr()) + vertexAlloc.getOffset();
deMemcpy(vertexDataPtr, fullScreenQuad.data(), static_cast<size_t>(vertexBufferSize));
vk::flushAlloc(vkd, device, vertexAlloc);
// Descriptor set layouts.
vk::DescriptorSetLayoutBuilder layoutBuilder;
std::vector<vk::Move<vk::VkDescriptorSetLayout>> descriptorSetLayouts;
// Create layouts for required amount of empty descriptor sets before the one that is actually used.
for (deUint32 descIdx = 0u; descIdx < m_params.descriptorSet; descIdx++)
{
descriptorSetLayouts.push_back(layoutBuilder.build(vkd, device));
}
// Create a layout for the descriptor set that is actually used.
layoutBuilder.addSingleBinding(m_params.type, vk::VK_SHADER_STAGE_FRAGMENT_BIT);
descriptorSetLayouts.push_back(layoutBuilder.build(vkd, device));
// Descriptor pool.
vk::DescriptorPoolBuilder poolBuilder;
poolBuilder.addType(m_params.type);
const auto descriptorPool = poolBuilder.build(vkd, device, vk::VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, m_params.descriptorSet + 1);
// Descriptor sets.
std::vector<vk::Move<vk::VkDescriptorSet>> descriptorSets;
for (deUint32 descIdx = 0u; descIdx < m_params.descriptorSet; descIdx++)
{
descriptorSets.push_back(vk::makeDescriptorSet(vkd, device, descriptorPool.get(), descriptorSetLayouts[descIdx].get()));
}
descriptorSets.push_back(vk::makeDescriptorSet(vkd, device, descriptorPool.get(), descriptorSetLayouts[m_params.descriptorSet].get()));
// Update descriptor set with the descriptor.
// IMPORTANT: the chosen sampler handle is used here.
vk::DescriptorSetUpdateBuilder updateBuilder;
const auto descriptorImageInfo = vk::makeDescriptorImageInfo(getSamplerHandle(), mainView.get(), getMainImageShaderLayout());
updateBuilder.writeSingle(descriptorSets[m_params.descriptorSet].get(), vk::DescriptorSetUpdateBuilder::Location::binding(0u), m_params.type, &descriptorImageInfo);
updateBuilder.update(vkd, device);
// Shader modules.
const auto vertexModule = vk::createShaderModule(vkd, device, m_context.getBinaryCollection().get("vert"), 0u);
const auto fragModule = vk::createShaderModule(vkd, device, m_context.getBinaryCollection().get("frag"), 0u);
// Render pass.
const vk::VkAttachmentDescription fbAttachment =
{
0u, // VkAttachmentDescriptionFlags flags;
kImageFormat, // VkFormat format;
vk::VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
vk::VK_ATTACHMENT_LOAD_OP_CLEAR, // VkAttachmentLoadOp loadOp;
vk::VK_ATTACHMENT_STORE_OP_STORE, // VkAttachmentStoreOp storeOp;
vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp stencilLoadOp;
vk::VK_ATTACHMENT_STORE_OP_DONT_CARE, // VkAttachmentStoreOp stencilStoreOp;
vk::VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // VkImageLayout finalLayout;
};
std::vector<vk::VkAttachmentDescription> attachmentDescs;
attachmentDescs.push_back(fbAttachment);
if (m_params.type == vk::VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT)
{
// Add it as a frame buffer attachment.
const vk::VkAttachmentDescription inputAttachment =
{
0u, // VkAttachmentDescriptionFlags flags;
kImageFormat, // VkFormat format;
vk::VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
vk::VK_ATTACHMENT_LOAD_OP_LOAD, // VkAttachmentLoadOp loadOp;
vk::VK_ATTACHMENT_STORE_OP_DONT_CARE, // VkAttachmentStoreOp storeOp;
vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp stencilLoadOp;
vk::VK_ATTACHMENT_STORE_OP_DONT_CARE, // VkAttachmentStoreOp stencilStoreOp;
getMainImageShaderLayout(), // VkImageLayout initialLayout;
getMainImageShaderLayout(), // VkImageLayout finalLayout;
};
attachmentDescs.push_back(inputAttachment);
}
std::vector<vk::VkAttachmentReference> inputAttachments;
if (m_params.type == vk::VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT)
{
const vk::VkAttachmentReference inputRef =
{
1u, // deUint32 attachment;
getMainImageShaderLayout(), // VkImageLayout layout;
};
inputAttachments.push_back(inputRef);
}
const vk::VkAttachmentReference colorRef =
{
0u, // deUint32 attachment;
vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // VkImageLayout layout;
};
const std::vector<vk::VkAttachmentReference> colorAttachments(1u, colorRef);
const vk::VkSubpassDescription subpass =
{
0u, // VkSubpassDescriptionFlags flags;
vk::VK_PIPELINE_BIND_POINT_GRAPHICS, // VkPipelineBindPoint pipelineBindPoint;
static_cast<deUint32>(inputAttachments.size()), // deUint32 inputAttachmentCount;
(inputAttachments.empty() ? nullptr : inputAttachments.data()), // const VkAttachmentReference* pInputAttachments;
static_cast<deUint32>(colorAttachments.size()), // deUint32 colorAttachmentCount;
colorAttachments.data(), // const VkAttachmentReference* pColorAttachments;
0u, // const VkAttachmentReference* pResolveAttachments;
nullptr, // const VkAttachmentReference* pDepthStencilAttachment;
0u, // deUint32 preserveAttachmentCount;
nullptr, // const deUint32* pPreserveAttachments;
};
const std::vector<vk::VkSubpassDescription> subpasses(1u, subpass);
const vk::VkRenderPassCreateInfo renderPassInfo =
{
vk::VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, // VkStructureType sType;
nullptr, // const void* pNext;
0u, // VkRenderPassCreateFlags flags;
static_cast<deUint32>(attachmentDescs.size()), // deUint32 attachmentCount;
attachmentDescs.data(), // const VkAttachmentDescription* pAttachments;
static_cast<deUint32>(subpasses.size()), // deUint32 subpassCount;
subpasses.data(), // const VkSubpassDescription* pSubpasses;
0u, // deUint32 dependencyCount;
nullptr, // const VkSubpassDependency* pDependencies;
};
const auto renderPass = vk::createRenderPass(vkd, device, &renderPassInfo);
// Framebuffer.
std::vector<vk::VkImageView> attachments;
attachments.push_back(fbView.get());
if (m_params.type == vk::VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT)
attachments.push_back(mainView.get());
const auto framebuffer = vk::makeFramebuffer(vkd, device, renderPass.get(), static_cast<deUint32>(attachments.size()), attachments.data(), kFramebufferExtent.width, kFramebufferExtent.height, kFramebufferExtent.depth);
// Pipeline layout.
const auto pipelineLayout = vk::makePipelineLayout(vkd, device, descriptorSetLayouts);
// Graphics pipeline.
const std::vector<vk::VkViewport> viewports(1u, vk::makeViewport(kFramebufferExtent));
const std::vector<vk::VkRect2D> scissors(1u, vk::makeRect2D(kFramebufferExtent));
const auto pipeline = vk::makeGraphicsPipeline(
vkd, device, pipelineLayout.get(),
vertexModule.get(), DE_NULL, DE_NULL, DE_NULL, fragModule.get(),
renderPass.get(), viewports, scissors);
// Command pool and command buffer.
const auto cmdPool = vk::createCommandPool(vkd, device, vk::VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueIndex);
const auto cmdBufferPtr = vk::allocateCommandBuffer(vkd, device, cmdPool.get(), vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY);
const auto cmdBuffer = cmdBufferPtr.get();
// Draw quad.
const vk::VkRect2D renderArea = vk::makeRect2D(kFramebufferExtent);
const tcu::Vec4 clearFbColor (0.0f, 0.0f, 0.0f, 1.0f);
const vk::VkDeviceSize vertexBufferOffset = 0ull;
const auto vtxBufferBarrier = vk::makeBufferMemoryBarrier(vk::VK_ACCESS_HOST_WRITE_BIT, vk::VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT, vertexBuffer.get(), 0ull, vertexBufferSize);
const auto preClearBarrier = vk::makeImageMemoryBarrier(0u, vk::VK_ACCESS_TRANSFER_WRITE_BIT, vk::VK_IMAGE_LAYOUT_UNDEFINED, vk::VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, mainImage.get(), colorSubresourceRange);
const auto postClearBarrier = vk::makeImageMemoryBarrier(vk::VK_ACCESS_TRANSFER_WRITE_BIT, vk::VK_ACCESS_SHADER_READ_BIT | vk::VK_ACCESS_COLOR_ATTACHMENT_READ_BIT,
vk::VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, getMainImageShaderLayout(), mainImage.get(), colorSubresourceRange);
const auto clearDescColor = vk::makeClearValueColor(kDescriptorColor);
vk::beginCommandBuffer(vkd, cmdBuffer);
vkd.cmdPipelineBarrier(cmdBuffer, vk::VK_PIPELINE_STAGE_HOST_BIT, vk::VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0u, 0u, nullptr, 1u, &vtxBufferBarrier, 0u, nullptr);
vkd.cmdPipelineBarrier(cmdBuffer, vk::VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, vk::VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 0u, nullptr, 0u, nullptr, 1u, &preClearBarrier);
vkd.cmdClearColorImage(cmdBuffer, mainImage.get(), vk::VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clearDescColor.color, 1u, &colorSubresourceRange);
vkd.cmdPipelineBarrier(cmdBuffer, vk::VK_PIPELINE_STAGE_TRANSFER_BIT, vk::VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | vk::VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0u, 0u, nullptr, 0u, nullptr, 1u, &postClearBarrier);
vk::beginRenderPass(vkd, cmdBuffer, renderPass.get(), framebuffer.get(), renderArea, clearFbColor);
vkd.cmdBindPipeline(cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.get());
vkd.cmdBindDescriptorSets(cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout.get(), m_params.descriptorSet, 1u, &descriptorSets[m_params.descriptorSet].get(), 0u, nullptr);
vkd.cmdBindVertexBuffers(cmdBuffer, 0u, 1u, &vertexBuffer.get(), &vertexBufferOffset);
vkd.cmdDraw(cmdBuffer, static_cast<deUint32>(fullScreenQuad.size()), 1u, 0u, 0u);
vk::endRenderPass(vkd, cmdBuffer);
const tcu::IVec2 copySize{static_cast<int>(kFramebufferExtent.width), static_cast<int>(kFramebufferExtent.height)};
vk::copyImageToBuffer(vkd, cmdBuffer, fbImage.get(), resultsBuffer.get(), copySize);
vk::endCommandBuffer(vkd, cmdBuffer);
vk::submitCommandsAndWait(vkd, device, queue, cmdBuffer);
m_context.resetCommandPoolForVKSC(device, *cmdPool);
// Check results.
const auto& resultsBufferAlloc = resultsBuffer.getAllocation();
vk::invalidateAlloc(vkd, device, resultsBufferAlloc);
const auto resultsBufferPtr = reinterpret_cast<const char*>(resultsBufferAlloc.getHostPtr()) + resultsBufferAlloc.getOffset();
const tcu::ConstPixelBufferAccess resultPixels {tcuFormat, copySize[0], copySize[1], 1, resultsBufferPtr};
bool pass = true;
for (int x = 0; pass && x < resultPixels.getWidth(); ++x)
for (int y = 0; pass && y < resultPixels.getHeight(); ++y)
for (int z = 0; pass && z < resultPixels.getDepth(); ++z)
{
const auto pixel = resultPixels.getPixel(x, y, z);
pass = (pixel == kDescriptorColor);
}
tcu::TestStatus status = tcu::TestStatus::pass("Pass");
if (!pass)
{
auto& log = m_context.getTestContext().getLog();
log << tcu::TestLog::Image("color", "Rendered image", resultPixels);
status = tcu::TestStatus::fail("Pixel mismatch; please check the rendered image");
}
return status;
}
tcu::TestCaseGroup* createSamplerlessWriteTests (tcu::TestContext& testCtx)
{
de::MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "samplerless", "Verify sampler unused with some descriptor image types"));
const std::vector<std::pair<vk::VkDescriptorType, std::string>> descriptorTypes =
{
std::make_pair(vk::VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, "sampled_img"),
std::make_pair(vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, "storage_img"),
std::make_pair(vk::VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, "input_attachment"),
};
const std::vector<std::pair<PointerCase, std::string>> pointerCases =
{
std::make_pair(PointerCase::ZERO, "sampler_zero"),
std::make_pair(PointerCase::ONE, "sampler_one"),
std::make_pair(PointerCase::DESTROYED, "sampler_destroyed"),
};
for (const auto& typeCase : descriptorTypes)
for (const auto& pointerCase : pointerCases)
for (deUint32 descriptorSet = 0u; descriptorSet < 2u; descriptorSet++)
{
std::string caseName = typeCase.second + "_" + pointerCase.second;
SamplerlessParams params {typeCase.first, pointerCase.first, descriptorSet};
if (descriptorSet > 0u)
{
caseName += "_set_" + std::to_string(descriptorSet);
}
group->addChild(new SamplerlessDescriptorWriteTestCase(testCtx, caseName, "", params));
}
return group.release();
}
class RandomDescriptorUpdateTestCase : public vkt::TestCase
{
public:
RandomDescriptorUpdateTestCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description);
virtual ~RandomDescriptorUpdateTestCase (void) {}
virtual void initPrograms (vk::SourceCollections& programCollection) const;
virtual vkt::TestInstance* createInstance (Context& context) const;
private:
};
class RandomDescriptorUpdateTestInstance : public vkt::TestInstance
{
public:
RandomDescriptorUpdateTestInstance (Context& context);
virtual ~RandomDescriptorUpdateTestInstance (void) {}
virtual tcu::TestStatus iterate (void);
static const vk::VkExtent3D kFramebufferExtent;
static const vk::VkFormat kImageFormat;
static const deUint32 kNumBuffers;
static const deUint32 kNumOffsets;
static const deUint32 kNumIterations;
private:
deRandom m_random;
};
const vk::VkExtent3D RandomDescriptorUpdateTestInstance::kFramebufferExtent = vk::makeExtent3D(64u, 64u, 1u);
const vk::VkFormat RandomDescriptorUpdateTestInstance::kImageFormat = vk::VK_FORMAT_R16G16B16A16_SFLOAT;
const deUint32 RandomDescriptorUpdateTestInstance::kNumBuffers = 3u;
const deUint32 RandomDescriptorUpdateTestInstance::kNumOffsets = 5u;
const deUint32 RandomDescriptorUpdateTestInstance::kNumIterations = 1000u;
RandomDescriptorUpdateTestCase::RandomDescriptorUpdateTestCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description)
: vkt::TestCase(testCtx, name, description)
{
}
void RandomDescriptorUpdateTestCase::initPrograms (vk::SourceCollections& programCollection) const
{
const std::string vertexShader =
"#version 450\n"
"layout(location=0) in vec4 position;\n"
"void main() { gl_Position = position; }\n";
programCollection.glslSources.add("vert") << glu::VertexSource(vertexShader);
std::ostringstream fragmentShader;
fragmentShader
<< "#version 450\n"
<< "layout(location = 0) out vec4 color_out;\n"
<< "layout(set = 0, binding = 0) uniform buf\n"
<< "{\n"
<< " vec4 data0;\n"
<< " vec4 data1;\n"
<< "};\n"
<< "void main()\n"
<< "{\n"
<< " color_out = data0 + data1;\n"
<< "}\n"
;
programCollection.glslSources.add("frag") << glu::FragmentSource(fragmentShader.str());
}
vkt::TestInstance* RandomDescriptorUpdateTestCase::createInstance (Context& context) const
{
return new RandomDescriptorUpdateTestInstance(context);
}
RandomDescriptorUpdateTestInstance::RandomDescriptorUpdateTestInstance(Context &context)
: vkt::TestInstance(context)
{
deRandom_init(&m_random, 0);
}
tcu::TestStatus RandomDescriptorUpdateTestInstance::iterate()
{
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();
const auto tcuFormat = vk::mapVkFormat(kImageFormat);
vk::DescriptorSetLayoutBuilder builder;
builder.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, vk::VK_SHADER_STAGE_FRAGMENT_BIT);
vk::Unique<vk::VkDescriptorSetLayout> layout (builder.build(vkd, device, (vk::VkDescriptorSetLayoutCreateFlags)0));
// Create descriptor pool
vk::Unique<vk::VkDescriptorPool> descriptorPool (vk::DescriptorPoolBuilder().addType(vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1).build(vkd, device, vk::VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1));
// Create descriptor set
const vk::VkDescriptorSetAllocateInfo setAllocateInfo =
{
vk::VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, // VkStructureType sType
DE_NULL, // const void* pNext
*descriptorPool, // VkDescriptorPool descriptorPool
1u, // deUint32 descriptorSetCount
&layout.get() // const VkDescriptorSetLayout* pSetLayouts
};
vk::Unique<vk::VkDescriptorSet> descriptorSet (allocateDescriptorSet(vkd, device, &setAllocateInfo));
// The maximum allowed buffer offset alignment is 256 bytes. Meaningful data is placed at these offsets.
const deUint32 bufferSize = 256u * kNumOffsets;
float bufferContents[kNumBuffers][bufferSize / 4];
float counter = 1.0f;
float sign = 1.0f;
deUint32 offset = 0;
deUint32 channelSelector = 0;
// The buffers are filled with a running counter in one of the channels.
// Both signed and unsigned values are used for each counter. Two vec4s
// are initialized at offsets of 256 bytes (the maximum allowed alignment).
// Everythin else is left as zero.
for (deUint32 b = 0; b < kNumBuffers; b++)
{
deMemset(bufferContents[b], 0, bufferSize);
for (deUint32 o = 0; o < kNumOffsets; o++)
{
offset = o * 64;
// Two vectors at every offset.
for (deUint32 v = 0; v < 2; v++)
{
// Only RGB channels are being tested.
for (deUint32 c = 0; c < 3; c++)
{
if (c == channelSelector)
{
bufferContents[b][offset++] = sign * counter;
}
else
{
bufferContents[b][offset++] = 0.0f;
}
}
// Keep alpha at one.
bufferContents[b][offset++] = 1.0f;
channelSelector = channelSelector + 1;
// All three channels have been filled in. Switch a sign or increase the counter.
if (channelSelector == 3)
{
channelSelector = 0;
if (sign == 1.0f)
{
sign = -1.0f;
}
else
{
sign = 1.0f;
counter += 1.0f;
}
}
}
}
}
const auto bufferInfo = vk::makeBufferCreateInfo(bufferSize, vk::VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT);
std::vector<std::shared_ptr<vk::BufferWithMemory>> buffers;
for (const auto& contents : bufferContents)
{
buffers.emplace_back(std::make_shared<vk::BufferWithMemory>(vkd, device, allocator, bufferInfo, vk::MemoryRequirement::HostVisible));
const auto& bufferAlloc = buffers.back()->getAllocation();
const auto bufferPtr = reinterpret_cast<char*>(bufferAlloc.getHostPtr()) + bufferAlloc.getOffset();
deMemcpy(bufferPtr, contents, bufferSize);
vk::flushAlloc(vkd, device, bufferAlloc);
}
// Create framebuffer image and view.
const vk::VkImageCreateInfo fbImgCreateInfo =
{
vk::VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType
DE_NULL, // const void* pNext
0u, // VkImageCreateFlags flags
vk::VK_IMAGE_TYPE_2D, // VkImageType imageType
kImageFormat, // VkFormat format
kFramebufferExtent, // VkExtent3D extent
1u, // deUint32 mipLevels
1u, // deUint32 arrayLayers
vk::VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples
vk::VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling
(vk::VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | // VkImageUsageFlags usage
vk::VK_IMAGE_USAGE_TRANSFER_SRC_BIT),
vk::VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode
1u, // deUint32 queueFamilyIndexCount
&queueIndex, // const deUint32* pQueueFamilyIndices
vk::VK_IMAGE_LAYOUT_UNDEFINED // VkImageLayout initialLayout
};
const vk::ImageWithMemory fbImage (vkd, device, allocator, fbImgCreateInfo, vk::MemoryRequirement::Any);
const auto colorSubresourceRange = vk::makeImageSubresourceRange(vk::VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
const auto fbView = vk::makeImageView(vkd, device, fbImage.get(), vk::VK_IMAGE_VIEW_TYPE_2D, kImageFormat, colorSubresourceRange);
// Buffer to copy rendering result to.
const vk::VkDeviceSize resultsBufferSize = static_cast<vk::VkDeviceSize>(static_cast<deUint32>(tcu::getPixelSize(tcuFormat)) * kFramebufferExtent.width * kFramebufferExtent.height * kFramebufferExtent.depth);
const auto resultsBufferInfo = vk::makeBufferCreateInfo(resultsBufferSize, vk::VK_BUFFER_USAGE_TRANSFER_DST_BIT);
const vk::BufferWithMemory resultsBuffer (vkd, device, allocator, resultsBufferInfo, vk::MemoryRequirement::HostVisible);
const std::vector<tcu::Vec4> fullScreenQuad =
{
{ -1.f, -1.f, 0.f, 1.f },
{ 1.f, -1.f, 0.f, 1.f },
{ -1.f, 1.f, 0.f, 1.f },
{ -1.f, 1.f, 0.f, 1.f },
{ 1.f, -1.f, 0.f, 1.f },
{ 1.f, 1.f, 0.f, 1.f }
};
// Vertex buffer.
const vk::VkDeviceSize vertexBufferSize = static_cast<vk::VkDeviceSize>(fullScreenQuad.size() * sizeof(decltype(fullScreenQuad)::value_type));
const auto vertexBufferInfo = vk::makeBufferCreateInfo(vertexBufferSize, vk::VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
const vk::BufferWithMemory vertexBuffer (vkd, device, allocator, vertexBufferInfo, vk::MemoryRequirement::HostVisible | vk::MemoryRequirement::Coherent);
// Copy data to vertex buffer.
const auto& vertexAlloc = vertexBuffer.getAllocation();
const auto vertexDataPtr = reinterpret_cast<char*>(vertexAlloc.getHostPtr()) + vertexAlloc.getOffset();
deMemcpy(vertexDataPtr, fullScreenQuad.data(), static_cast<size_t>(vertexBufferSize));
vk::flushAlloc(vkd, device, vertexAlloc);
// Shader modules.
const auto vertexModule = vk::createShaderModule(vkd, device, m_context.getBinaryCollection().get("vert"), 0u);
const auto fragModule = vk::createShaderModule(vkd, device, m_context.getBinaryCollection().get("frag"), 0u);
// Render pass.
const vk::VkAttachmentDescription fbAttachment =
{
0u, // VkAttachmentDescriptionFlags flags
kImageFormat, // VkFormat format
vk::VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples
vk::VK_ATTACHMENT_LOAD_OP_LOAD, // VkAttachmentLoadOp loadOp
vk::VK_ATTACHMENT_STORE_OP_STORE, // VkAttachmentStoreOp storeOp
vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp stencilLoadOp
vk::VK_ATTACHMENT_STORE_OP_DONT_CARE, // VkAttachmentStoreOp stencilStoreOp
vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // VkImageLayout initialLayout
vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL // VkImageLayout finalLayout
};
std::vector<vk::VkAttachmentDescription> attachmentDescs;
attachmentDescs.push_back(fbAttachment);
const vk::VkAttachmentReference colorRef =
{
0u, // deUint32 attachment
vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // VkImageLayout layout
};
const std::vector<vk::VkAttachmentReference> colorAttachments(1u, colorRef);
const vk::VkSubpassDescription subpass =
{
0u, // VkSubpassDescriptionFlags flags
vk::VK_PIPELINE_BIND_POINT_GRAPHICS, // VkPipelineBindPoint pipelineBindPoint
0u, // deUint32 inputAttachmentCount
DE_NULL, // const VkAttachmentReference* pInputAttachments
static_cast<deUint32>(colorAttachments.size()), // deUint32 colorAttachmentCount
colorAttachments.data(), // const VkAttachmentReference* pColorAttachments
0u, // const VkAttachmentReference* pResolveAttachments
DE_NULL, // const VkAttachmentReference* pDepthStencilAttachment
0u, // deUint32 preserveAttachmentCount
DE_NULL // const deUint32* pPreserveAttachments
};
const std::vector<vk::VkSubpassDescription> subpasses (1u, subpass);
const vk::VkRenderPassCreateInfo renderPassInfo =
{
vk::VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, // VkStructureType sType
DE_NULL, // const void* pNext
0u, // VkRenderPassCreateFlags flags
static_cast<deUint32>(attachmentDescs.size()), // deUint32 attachmentCount
attachmentDescs.data(), // const VkAttachmentDescription* pAttachments
static_cast<deUint32>(subpasses.size()), // deUint32 subpassCount
subpasses.data(), // const VkSubpassDescription* pSubpasses
0u, // deUint32 dependencyCount
DE_NULL, // const VkSubpassDependency* pDependencies
};
const auto renderPass = vk::createRenderPass(vkd, device, &renderPassInfo);
// Framebuffer.
std::vector<vk::VkImageView> attachments;
attachments.push_back(fbView.get());
const auto framebuffer = vk::makeFramebuffer(vkd, device, renderPass.get(), static_cast<deUint32>(attachments.size()), attachments.data(), kFramebufferExtent.width, kFramebufferExtent.height, kFramebufferExtent.depth);
// Pipeline layout.
const auto pipelineLayout = vk::makePipelineLayout(vkd, device, layout.get());
// Graphics pipeline.
const std::vector<vk::VkViewport> viewports (1u, vk::makeViewport(kFramebufferExtent));
const std::vector<vk::VkRect2D> scissors (1u, vk::makeRect2D(kFramebufferExtent));
// Use additive alpha blending to accumulate results from all iterations.
const vk::VkPipelineColorBlendAttachmentState colorBlendAttachmentState =
{
VK_TRUE, // VkBool32 blendEnable
vk::VK_BLEND_FACTOR_ONE, // VkBlendFactor srcColorBlendFactor
vk::VK_BLEND_FACTOR_ONE, // VkBlendFactor dstColorBlendFactor
vk::VK_BLEND_OP_ADD, // VkBlendOp colorBlendOp
vk::VK_BLEND_FACTOR_ONE, // VkBlendFactor srcAlphaBlendFactor
vk::VK_BLEND_FACTOR_ZERO, // VkBlendFactor dstAlphaBlendFactor
vk::VK_BLEND_OP_ADD, // VkBlendOp alphaBlendOp
vk::VK_COLOR_COMPONENT_R_BIT // VkColorComponentFlags colorWriteMask
| vk::VK_COLOR_COMPONENT_G_BIT
| vk::VK_COLOR_COMPONENT_B_BIT
| vk::VK_COLOR_COMPONENT_A_BIT
};
const vk::VkPipelineColorBlendStateCreateInfo colorBlendState =
{
vk::VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, // VkStructureType sType
DE_NULL, // const void* pNext
0u, // VkPipelineColorBlendStateCreateFlags flags
VK_FALSE, // VkBool32 logicOpEnable
vk::VK_LOGIC_OP_CLEAR, // VkLogicOp logicOp
1u, // deUint32 attachmentCount
&colorBlendAttachmentState, // const VkPipelineColorBlendAttachmentState* pAttachments
{ 1.0f, 1.0f, 1.0f, 1.0f } // float blendConstants[4]
};
const auto pipeline = vk::makeGraphicsPipeline(vkd, device, pipelineLayout.get(),
vertexModule.get(), DE_NULL, DE_NULL, DE_NULL, fragModule.get(),
renderPass.get(), viewports, scissors, vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
0u, 0u, DE_NULL, DE_NULL, DE_NULL, DE_NULL, &colorBlendState);
// Command pool and command buffer.
const auto cmdPool = vk::createCommandPool(vkd, device, vk::VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueIndex);
const auto cmdBufferPtr = vk::allocateCommandBuffer(vkd, device, cmdPool.get(), vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY);
const auto cmdBuffer = cmdBufferPtr.get();
const vk::VkRect2D renderArea = vk::makeRect2D(kFramebufferExtent);
const vk::VkDeviceSize vertexBufferOffset = 0ull;
const auto vtxBufferBarrier = vk::makeBufferMemoryBarrier(vk::VK_ACCESS_HOST_WRITE_BIT, vk::VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT, vertexBuffer.get(), 0ull, vertexBufferSize);
const auto fbBarrier = vk::makeImageMemoryBarrier(0u, vk::VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, vk::VK_IMAGE_LAYOUT_UNDEFINED, vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, fbImage.get(), colorSubresourceRange);
vk::VkClearValue clearValue;
clearValue.color.float32[0] = 0.0f;
clearValue.color.float32[1] = 0.0f;
clearValue.color.float32[2] = 0.0f;
clearValue.color.float32[3] = 1.0f;
const vk::VkClearAttachment clearAttachment =
{
vk::VK_IMAGE_ASPECT_COLOR_BIT, // VkImageAspectFlags aspectMask
0u, // uint32_t colorAttachment
clearValue // VkClearValue clearValue
};
const vk::VkClearRect clearRect =
{
vk::makeRect2D(kFramebufferExtent), // VkRect2D rect
0u, // uint32_t baseArrayLayer
1u // uint32_t layerCount
};
vk::beginCommandBuffer(vkd, cmdBuffer);
vkd.cmdPipelineBarrier(cmdBuffer, vk::VK_PIPELINE_STAGE_HOST_BIT, vk::VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0u,
0u, DE_NULL, 1u, &vtxBufferBarrier, 0u, DE_NULL);
vkd.cmdPipelineBarrier(cmdBuffer, vk::VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, vk::VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0u,
0u, DE_NULL, 0u, DE_NULL, 1u, &fbBarrier);
vk::endCommandBuffer(vkd, cmdBuffer);
vk::submitCommandsAndWait(vkd, device, queue, cmdBuffer);
m_context.resetCommandPoolForVKSC(device, *cmdPool);
struct DescriptorWrite
{
deUint32 bufferId; // Which buffer to use for the descriptor update.
vk::VkDeviceSize offset; // The offset for the descriptor update.
vk::VkDeviceSize range; // The range for the descriptor update.
};
// Each iteration operates on a descriptor mutation which decides the source of the descriptor update.
struct DescriptorMutation
{
deBool update; // Determines if a descriptor update is performed.
deUint32 numDraws; // The number of consecutive draw calls in a loop.
std::vector<DescriptorWrite> writes; // Multiple redundant writes can be performed.
// Other ideas to implement:
// - Sometimes also update the buffer contents.
// - Multiple descriptor sets.
};
std::vector<DescriptorMutation> descriptorMutations;
// Keep track of the expected result while generating the mutations.
tcu::Vec4 uboValue0;
tcu::Vec4 uboValue1;
tcu::Vec4 expectedColor (0.0f, 0.0f, 0.0f, 1.0f);
DescriptorWrite descWrite = { 0u, 0u, 32u };
for (deUint32 i = 0; i < kNumIterations; i++)
{
while (true)
{
tcu::Vec4 val0 = uboValue0;
tcu::Vec4 val1 = uboValue1;
deUint32 numWrites = 1u;
// Sometimes do redundant descriptor writes.
if (deRandom_getUint32(&m_random) % 10 == 0)
numWrites = deRandom_getUint32(&m_random) % 20 + 1;
std::vector<DescriptorWrite> writes;
for (deUint32 w = 0; w < numWrites; w++)
{
// The first half: Most of the times change the offset but sometimes the buffer.
// The second half: Most of the times change the buffer but sometimes change the offset.
bool firstHalf = i < kNumIterations / 2;
bool rare = (deRandom_getUint32(&m_random) % 100u >= (firstHalf ? 98u : 80u));
// firstHalf rare change
// --------------------------------
// 1 0 Offset
// 1 1 Buffer
// 0 0 Buffer
// 0 1 Offset
//
// This has a XOR pattern
if (firstHalf ^ rare)
descWrite.offset = (deRandom_getUint32(&m_random) % kNumOffsets) * 256u;
else
descWrite.bufferId = deRandom_getUint32(&m_random) % kNumBuffers;
writes.push_back(descWrite);
}
DescriptorMutation mutation = {i == 0 ? true : deRandom_getBool(&m_random),
deRandom_getUint32(&m_random) % 10u, writes};
const auto& lastWrite = mutation.writes.back();
if (mutation.update)
{
for (int c = 0; c < 3; c++)
{
val0[c] = bufferContents[lastWrite.bufferId][lastWrite.offset / 4 + c];
val1[c] = bufferContents[lastWrite.bufferId][lastWrite.offset / 4 + 4 + c];
// Sanity check we are reading expected values.
DE_ASSERT(val0[c] >= -counter && val0[c] <= counter);
DE_ASSERT(val1[c] >= -counter && val1[c] <= counter);
}
}
tcu::Vec4 color = expectedColor + (val0 + val1) * tcu::Vec4(static_cast<float>(mutation.numDraws));
// 16-bit float can precisely present integers from -2048..2048. Continue randomizing the mutation
// until we stay in this range.
if (color[0] >= -2048.0f && color[0] <= 2048.0f && color[1] >= -2048.0f && color[1] <= 2048.0f
&& color[2] >= -2048.0f && color[2] <= 2048.0f)
{
descriptorMutations.push_back(mutation);
uboValue0 = val0;
uboValue1 = val1;
expectedColor = color;
break;
}
else
{
// Randomize both buffer and offset for a better chance to hit a
// mutation that pushes the values back to the desired range.
descWrite.offset = (deRandom_getUint32(&m_random) % kNumOffsets) * 256u;
descWrite.bufferId = deRandom_getUint32(&m_random) % kNumBuffers;
}
}
}
bool first = true;
for (auto mutation : descriptorMutations)
{
if (mutation.update)
{
for (const auto &write : mutation.writes)
{
const vk::VkDescriptorBufferInfo descriptorInfo =
{
buffers[write.bufferId]->get(), // VkBuffer buffer
write.offset, // VkDeviceSize offset
write.range // VkDeviceSize range
};
const vk::VkWriteDescriptorSet descriptorWrite =
{
vk::VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, // VkStructureTypes Type
DE_NULL, // const void* pNext
*descriptorSet, // VkDescriptorSet dstSet
0, // deUint32 dstBinding
0, // deUint32 dstArrayElement
1u, // deUint32 descriptorCount
vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, // VkDescriptorType descriptorType
DE_NULL, // const VkDescriptorImageInfo* pImageInfo
&descriptorInfo, // const VkDescriptorBufferInfo* pBufferInfo
DE_NULL // const VkBufferView* pTexelBufferView
};
vkd.updateDescriptorSets(device, 1, &descriptorWrite, 0, DE_NULL);
}
}
vk::beginCommandBuffer(vkd, cmdBuffer);
vk::beginRenderPass(vkd, cmdBuffer, renderPass.get(), framebuffer.get(), renderArea);
vkd.cmdBindPipeline(cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.get());
// Clear the frame buffer during the first iteration.
if (first)
{
vkd.cmdClearAttachments(cmdBuffer, 1u, &clearAttachment, 1u, &clearRect);
first = false;
}
vkd.cmdBindDescriptorSets(cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout.get(), 0u, 1u,
&descriptorSet.get(), 0u, nullptr);
vkd.cmdBindVertexBuffers(cmdBuffer, 0u, 1u, &vertexBuffer.get(), &vertexBufferOffset);
for (deUint32 i = 0u; i < mutation.numDraws; i++)
vkd.cmdDraw(cmdBuffer, static_cast<deUint32>(fullScreenQuad.size()), 1u, 0u, 0u);
vk::endRenderPass(vkd, cmdBuffer);
vk::endCommandBuffer(vkd, cmdBuffer);
vk::submitCommandsAndWait(vkd, device, queue, cmdBuffer);
m_context.resetCommandPoolForVKSC(device, *cmdPool);
}
vk::beginCommandBuffer(vkd, cmdBuffer);
const tcu::IVec2 copySize{static_cast<int>(kFramebufferExtent.width),
static_cast<int>(kFramebufferExtent.height)};
vk::copyImageToBuffer(vkd, cmdBuffer, fbImage.get(), resultsBuffer.get(), copySize);
vk::endCommandBuffer(vkd, cmdBuffer);
vk::submitCommandsAndWait(vkd, device, queue, cmdBuffer);
m_context.resetCommandPoolForVKSC(device, *cmdPool);
// Check results.
const auto& resultsBufferAlloc = resultsBuffer.getAllocation();
vk::invalidateAlloc(vkd, device, resultsBufferAlloc);
const auto resultsBufferPtr = reinterpret_cast<const char*>(resultsBufferAlloc.getHostPtr()) + resultsBufferAlloc.getOffset();
const tcu::ConstPixelBufferAccess resultPixels {tcuFormat, copySize[0], copySize[1], 1, resultsBufferPtr};
// The test only operates on integers, so a tolerance of 0.5 should work.
const float tolerance = 0.5f;
bool pass = true;
for (int x = 0; pass && x < resultPixels.getWidth(); ++x)
for (int y = 0; pass && y < resultPixels.getHeight(); ++y)
for (int z = 0; pass && z < resultPixels.getDepth(); ++z)
{
const auto pixel = resultPixels.getPixel(x, y, z);
for (int c = 0; c < 3; c++)
if (fabs(pixel[c] - expectedColor[c]) > tolerance)
pass = false;
}
tcu::TestStatus status = tcu::TestStatus::pass("Pass");
if (!pass)
{
m_context.getTestContext().getLog() << tcu::TestLog::Image("color", "Rendered image", resultPixels);
status = tcu::TestStatus::fail("Pixel mismatch; please check the rendered image");
}
return status;
}
tcu::TestCaseGroup* createRandomDescriptorUpdateTests (tcu::TestContext& testCtx)
{
de::MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "random", "Update descriptors randomly between draws"));
group->addChild(new RandomDescriptorUpdateTestCase(testCtx, "uniform_buffer", ""));
return group.release();
}
} // anonymous
tcu::TestCaseGroup* createDescriptorUpdateTests (tcu::TestContext& testCtx)
{
de::MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "descriptor_update", "Update descriptor sets"));
group->addChild(createEmptyDescriptorUpdateTests(testCtx));
group->addChild(createSamplerlessWriteTests(testCtx));
group->addChild(createRandomDescriptorUpdateTests(testCtx));
#ifndef CTS_USES_VULKANSC
group->addChild(createDescriptorUpdateASTests(testCtx));
#endif // CTS_USES_VULKANSC
return group.release();
}
} // BindingModel
} // vkt