/*------------------------------------------------------------------------- * 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 #include #include #include #include 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 layout (builder.build(vki, device, (vk::VkDescriptorSetLayoutCreateFlags)0)); // Create descriptor pool vk::Unique 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 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 buffer (createBuffer(vki, device, &bufferCreateInfo)); const vk::VkMemoryRequirements requirements = vk::getBufferMemoryRequirements(vki, device, *buffer); de::MovePtr 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 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(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(static_cast(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 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(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(vertexAlloc.getHostPtr()) + vertexAlloc.getOffset(); deMemcpy(vertexDataPtr, fullScreenQuad.data(), static_cast(vertexBufferSize)); vk::flushAlloc(vkd, device, vertexAlloc); // Descriptor set layouts. vk::DescriptorSetLayoutBuilder layoutBuilder; std::vector> 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> 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 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 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 colorAttachments(1u, colorRef); const vk::VkSubpassDescription subpass = { 0u, // VkSubpassDescriptionFlags flags; vk::VK_PIPELINE_BIND_POINT_GRAPHICS, // VkPipelineBindPoint pipelineBindPoint; static_cast(inputAttachments.size()), // deUint32 inputAttachmentCount; (inputAttachments.empty() ? nullptr : inputAttachments.data()), // const VkAttachmentReference* pInputAttachments; static_cast(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 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(attachmentDescs.size()), // deUint32 attachmentCount; attachmentDescs.data(), // const VkAttachmentDescription* pAttachments; static_cast(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 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(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 viewports(1u, vk::makeViewport(kFramebufferExtent)); const std::vector 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(fullScreenQuad.size()), 1u, 0u, 0u); vk::endRenderPass(vkd, cmdBuffer); const tcu::IVec2 copySize{static_cast(kFramebufferExtent.width), static_cast(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(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 group(new tcu::TestCaseGroup(testCtx, "samplerless", "Verify sampler unused with some descriptor image types")); const std::vector> 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> 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 layout (builder.build(vkd, device, (vk::VkDescriptorSetLayoutCreateFlags)0)); // Create descriptor pool vk::Unique 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 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> buffers; for (const auto& contents : bufferContents) { buffers.emplace_back(std::make_shared(vkd, device, allocator, bufferInfo, vk::MemoryRequirement::HostVisible)); const auto& bufferAlloc = buffers.back()->getAllocation(); const auto bufferPtr = reinterpret_cast(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(static_cast(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 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(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(vertexAlloc.getHostPtr()) + vertexAlloc.getOffset(); deMemcpy(vertexDataPtr, fullScreenQuad.data(), static_cast(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 attachmentDescs; attachmentDescs.push_back(fbAttachment); const vk::VkAttachmentReference colorRef = { 0u, // deUint32 attachment vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // VkImageLayout layout }; const std::vector 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(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 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(attachmentDescs.size()), // deUint32 attachmentCount attachmentDescs.data(), // const VkAttachmentDescription* pAttachments static_cast(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 attachments; attachments.push_back(fbView.get()); const auto framebuffer = vk::makeFramebuffer(vkd, device, renderPass.get(), static_cast(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 viewports (1u, vk::makeViewport(kFramebufferExtent)); const std::vector 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 writes; // Multiple redundant writes can be performed. // Other ideas to implement: // - Sometimes also update the buffer contents. // - Multiple descriptor sets. }; std::vector 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 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(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(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(kFramebufferExtent.width), static_cast(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(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 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 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