/*------------------------------------------------------------------------- * Vulkan Conformance Tests * ------------------------ * * Copyright (c) 2021 The Khronos Group Inc. * Copyright (c) 2021 Valve Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *//*! * \file * \brief Tests for VK_VALVE_mutable_descriptor_type. *//*--------------------------------------------------------------------*/ #include "vktBindingValveMutableTests.hpp" #include "vktTestCase.hpp" #include "vkDefs.hpp" #include "vkRefUtil.hpp" #include "vkQueryUtil.hpp" #include "vkImageWithMemory.hpp" #include "vkBufferWithMemory.hpp" #include "vkTypeUtil.hpp" #include "vkObjUtil.hpp" #include "vkBarrierUtil.hpp" #include "vkCmdUtil.hpp" #include "vkBuilderUtil.hpp" #include "vkRayTracingUtil.hpp" #include "deUniquePtr.hpp" #include "deSTLUtil.hpp" #include "deStringUtil.hpp" #include #include #include #include #include #include namespace vkt { namespace BindingModel { namespace { using namespace vk; deUint32 getDescriptorNumericValue (deUint32 iteration, deUint32 bindingIdx, deUint32 descriptorIdx = 0u) { // When assigning numeric values for the descriptor contents, each descriptor will get 0x5aIIBBDD. II is an octed containing the // iteration index. BB is an octet containing the binding index and DD is the descriptor index inside that binding. constexpr deUint32 kNumericValueBase = 0x5a000000u; return (kNumericValueBase | ((iteration & 0xFFu) << 16) | ((bindingIdx & 0xFFu) << 8) | (descriptorIdx & 0xFFu)); } deUint16 getAccelerationStructureOffsetX (deUint32 descriptorNumericValue) { // Keep the lowest 16 bits (binding and descriptor idx) as the offset. return static_cast(descriptorNumericValue); } // Value that will be stored in the output buffer to signal success reading values. deUint32 getExpectedOutputBufferValue () { return 2u; } // This value will be stored in an image to be sampled when checking descriptors containing samplers alone. deUint32 getExternalSampledImageValue () { return 0x41322314u; } // Value that will be ORed with the descriptor value before writing. deUint32 getStoredValueMask () { return 0xFF000000u; } VkFormat getDescriptorImageFormat () { return VK_FORMAT_R32_UINT; } VkExtent3D getDefaultExtent () { return makeExtent3D(1u, 1u, 1u); } // Convert value to hexadecimal. std::string toHex (deUint32 val) { std::ostringstream s; s << "0x" << std::hex << val << "u"; return s.str(); } // Returns the list of descriptor types that cannot be part of a mutable descriptor. std::vector getForbiddenMutableTypes () { return std::vector { VK_DESCRIPTOR_TYPE_MUTABLE_VALVE, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT, }; } // Returns the list of descriptor types that are mandatory for the extension. std::vector getMandatoryMutableTypes () { return std::vector { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER }; } // This helps quickly transform a vector of descriptor types into a bitmask, which makes it easier to check some conditions. enum DescriptorTypeFlagBits { DTFB_SAMPLER = (1 << 0), DTFB_COMBINED_IMAGE_SAMPLER = (1 << 1), DTFB_SAMPLED_IMAGE = (1 << 2), DTFB_STORAGE_IMAGE = (1 << 3), DTFB_UNIFORM_TEXEL_BUFFER = (1 << 4), DTFB_STORAGE_TEXEL_BUFFER = (1 << 5), DTFB_UNIFORM_BUFFER = (1 << 6), DTFB_STORAGE_BUFFER = (1 << 7), DTFB_UNIFORM_BUFFER_DYNAMIC = (1 << 8), DTFB_STORAGE_BUFFER_DYNAMIC = (1 << 9), DTFB_INPUT_ATTACHMENT = (1 << 10), DTFB_INLINE_UNIFORM_BLOCK_EXT = (1 << 11), DTFB_ACCELERATION_STRUCTURE_KHR = (1 << 12), DTFB_ACCELERATION_STRUCTURE_NV = (1 << 13), DTFB_MUTABLE_VALVE = (1 << 14), }; using DescriptorTypeFlags = deUint32; // Convert type to its corresponding flag bit. DescriptorTypeFlagBits toDescriptorTypeFlagBit (VkDescriptorType descriptorType) { switch (descriptorType) { case VK_DESCRIPTOR_TYPE_SAMPLER: return DTFB_SAMPLER; case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: return DTFB_COMBINED_IMAGE_SAMPLER; case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: return DTFB_SAMPLED_IMAGE; case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: return DTFB_STORAGE_IMAGE; case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: return DTFB_UNIFORM_TEXEL_BUFFER; case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: return DTFB_STORAGE_TEXEL_BUFFER; case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: return DTFB_UNIFORM_BUFFER; case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: return DTFB_STORAGE_BUFFER; case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: return DTFB_UNIFORM_BUFFER_DYNAMIC; case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: return DTFB_STORAGE_BUFFER_DYNAMIC; case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: return DTFB_INPUT_ATTACHMENT; case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT: return DTFB_INLINE_UNIFORM_BLOCK_EXT; case VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR: return DTFB_ACCELERATION_STRUCTURE_KHR; case VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV: return DTFB_ACCELERATION_STRUCTURE_NV; case VK_DESCRIPTOR_TYPE_MUTABLE_VALVE: return DTFB_MUTABLE_VALVE; default: break; } // Unreachable. DE_ASSERT(false); return DTFB_SAMPLER; } // Convert vector of descriptor types to a bitfield. DescriptorTypeFlags toDescriptorTypeFlags (const std::vector& types) { DescriptorTypeFlags result = 0u; for (const auto& t : types) result |= toDescriptorTypeFlagBit(t); return result; } // Convert bitfield to vector of descriptor types. std::vector toDescriptorTypeVector (DescriptorTypeFlags bitfield) { std::vector result; if (bitfield & DTFB_SAMPLER) result.push_back(VK_DESCRIPTOR_TYPE_SAMPLER); if (bitfield & DTFB_COMBINED_IMAGE_SAMPLER) result.push_back(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); if (bitfield & DTFB_SAMPLED_IMAGE) result.push_back(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE); if (bitfield & DTFB_STORAGE_IMAGE) result.push_back(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE); if (bitfield & DTFB_UNIFORM_TEXEL_BUFFER) result.push_back(VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER); if (bitfield & DTFB_STORAGE_TEXEL_BUFFER) result.push_back(VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER); if (bitfield & DTFB_UNIFORM_BUFFER) result.push_back(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER); if (bitfield & DTFB_STORAGE_BUFFER) result.push_back(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); if (bitfield & DTFB_UNIFORM_BUFFER_DYNAMIC) result.push_back(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC); if (bitfield & DTFB_STORAGE_BUFFER_DYNAMIC) result.push_back(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC); if (bitfield & DTFB_INPUT_ATTACHMENT) result.push_back(VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT); if (bitfield & DTFB_INLINE_UNIFORM_BLOCK_EXT) result.push_back(VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT); if (bitfield & DTFB_ACCELERATION_STRUCTURE_KHR) result.push_back(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR); if (bitfield & DTFB_ACCELERATION_STRUCTURE_NV) result.push_back(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV); if (bitfield & DTFB_MUTABLE_VALVE) result.push_back(VK_DESCRIPTOR_TYPE_MUTABLE_VALVE); return result; } // How to create the source set when copying descriptors from another set. // * MUTABLE means to transform bindings into mutable bindings. // * NONMUTABLE means to transform bindings into non-mutable bindings. enum class SourceSetStrategy { MUTABLE = 0, NONMUTABLE, NO_SOURCE, }; enum class PoolMutableStrategy { KEEP_TYPES = 0, EXPAND_TYPES, NO_TYPES, }; // Type of information that's present in VkWriteDescriptorSet. enum class WriteType { IMAGE_INFO = 0, BUFFER_INFO, BUFFER_VIEW, ACCELERATION_STRUCTURE_INFO, }; struct WriteInfo { WriteType writeType; union { VkDescriptorImageInfo imageInfo; VkDescriptorBufferInfo bufferInfo; VkBufferView bufferView; VkWriteDescriptorSetAccelerationStructureKHR asInfo; }; explicit WriteInfo (const VkDescriptorImageInfo& info_) : writeType(WriteType::IMAGE_INFO) , imageInfo(info_) {} explicit WriteInfo (const VkDescriptorBufferInfo& info_) : writeType(WriteType::BUFFER_INFO) , bufferInfo(info_) {} explicit WriteInfo (VkBufferView view_) : writeType(WriteType::BUFFER_VIEW) , bufferView(view_) {} explicit WriteInfo (const VkWriteDescriptorSetAccelerationStructureKHR& asInfo_) : writeType(WriteType::ACCELERATION_STRUCTURE_INFO) , asInfo(asInfo_) {} }; // Resource backing up a single binding. enum class ResourceType { SAMPLER = 0, IMAGE, COMBINED_IMAGE_SAMPLER, BUFFER, BUFFER_VIEW, ACCELERATION_STRUCTURE, }; // Type of resource backing up a particular descriptor type. ResourceType toResourceType (VkDescriptorType descriptorType) { ResourceType resourceType = ResourceType::SAMPLER; switch (descriptorType) { case VK_DESCRIPTOR_TYPE_SAMPLER: resourceType = ResourceType::SAMPLER; break; case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: resourceType = ResourceType::COMBINED_IMAGE_SAMPLER; break; case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: resourceType = ResourceType::IMAGE; break; case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: resourceType = ResourceType::BUFFER_VIEW; break; case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: resourceType = ResourceType::BUFFER; break; case VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR: resourceType = ResourceType::ACCELERATION_STRUCTURE; break; default: DE_ASSERT(false); break; } return resourceType; } bool isShaderWritable (VkDescriptorType descriptorType) { return (descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER || descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE || descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER); } Move makeDefaultSampler (const DeviceInterface& vkd, VkDevice device) { const VkSamplerCreateInfo samplerCreateInfo = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, // VkStructureType sType; nullptr, // const void* pNext; 0u, // VkSamplerCreateFlags flags; VK_FILTER_NEAREST, // VkFilter magFilter; VK_FILTER_NEAREST, // VkFilter minFilter; VK_SAMPLER_MIPMAP_MODE_NEAREST, // VkSamplerMipmapMode mipmapMode; VK_SAMPLER_ADDRESS_MODE_REPEAT, // VkSamplerAddressMode addressModeU; VK_SAMPLER_ADDRESS_MODE_REPEAT, // VkSamplerAddressMode addressModeV; VK_SAMPLER_ADDRESS_MODE_REPEAT, // VkSamplerAddressMode addressModeW; 0.f, // float mipLodBias; VK_FALSE, // VkBool32 anisotropyEnable; 1.f, // float maxAnisotropy; VK_FALSE, // VkBool32 compareEnable; VK_COMPARE_OP_ALWAYS, // VkCompareOp compareOp; 0.f, // float minLod; 0.f, // float maxLod; VK_BORDER_COLOR_INT_TRANSPARENT_BLACK, // VkBorderColor borderColor; VK_FALSE, // VkBool32 unnormalizedCoordinates; }; return createSampler(vkd, device, &samplerCreateInfo); } de::MovePtr makeDefaultImage (const DeviceInterface& vkd, VkDevice device, Allocator& alloc) { const auto extent = makeExtent3D(1u, 1u, 1u); const VkImageUsageFlags usageFlags = ( VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT); const VkImageCreateInfo imageCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType; nullptr, // const void* pNext; 0u, // VkImageCreateFlags flags; VK_IMAGE_TYPE_2D, // VkImageType imageType; getDescriptorImageFormat(), // VkFormat format; extent, // VkExtent3D extent; 1u, // deUint32 mipLevels; 1u, // deUint32 arrayLayers; VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples; VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling; usageFlags, // VkImageUsageFlags usage; VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode; 0u, // deUint32 queueFamilyIndexCount; nullptr, // const deUint32* pQueueFamilyIndices; VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout; }; return de::MovePtr(new ImageWithMemory(vkd, device, alloc, imageCreateInfo, MemoryRequirement::Any)); } Move makeDefaultImageView (const DeviceInterface& vkd, VkDevice device, VkImage image) { const auto subresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u); return makeImageView(vkd, device, image, VK_IMAGE_VIEW_TYPE_2D, getDescriptorImageFormat(), subresourceRange); } de::MovePtr makeDefaultBuffer (const DeviceInterface& vkd, VkDevice device, Allocator& alloc, deUint32 numElements = 1u) { const VkBufferUsageFlags bufferUsage = ( VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT); const auto bufferSize = static_cast(sizeof(deUint32) * static_cast(numElements)); const auto bufferCreateInfo = makeBufferCreateInfo(bufferSize, bufferUsage); return de::MovePtr(new BufferWithMemory(vkd, device, alloc, bufferCreateInfo, MemoryRequirement::HostVisible)); } Move makeDefaultBufferView (const DeviceInterface& vkd, VkDevice device, VkBuffer buffer) { const auto bufferOffset = static_cast(0); const auto bufferSize = static_cast(sizeof(deUint32)); return makeBufferView(vkd, device, buffer, getDescriptorImageFormat(), bufferOffset, bufferSize); } struct AccelerationStructureData { using TLASPtr = de::MovePtr; using BLASPtr = de::MovePtr; TLASPtr tlas; BLASPtr blas; void swap (AccelerationStructureData& other) { auto myTlasPtr = tlas.release(); auto myBlasPtr = blas.release(); auto otherTlasPtr = other.tlas.release(); auto otherBlasPtr = other.blas.release(); tlas = TLASPtr(otherTlasPtr); blas = BLASPtr(otherBlasPtr); other.tlas = TLASPtr(myTlasPtr); other.blas = BLASPtr(myBlasPtr); } AccelerationStructureData () : tlas() , blas() {} AccelerationStructureData (AccelerationStructureData&& other) : AccelerationStructureData() { swap(other); } AccelerationStructureData& operator= (AccelerationStructureData&& other) { swap(other); return *this; } }; AccelerationStructureData makeDefaultAccelerationStructure (const DeviceInterface& vkd, VkDevice device, VkCommandBuffer cmdBuffer, Allocator& alloc, bool triangles, deUint16 offsetX) { AccelerationStructureData data; // Triangle around (offsetX, 0) with depth 5.0. const float middleX = static_cast(offsetX); const float leftX = middleX - 0.5f; const float rightX = middleX + 0.5f; const float topY = 0.5f; const float bottomY = -0.5f; const float depth = 5.0f; std::vector vertices; if (triangles) { vertices.reserve(3u); vertices.emplace_back(middleX, topY, depth); vertices.emplace_back(rightX, bottomY, depth); vertices.emplace_back(leftX, bottomY, depth); } else { vertices.reserve(2u); vertices.emplace_back(leftX, bottomY, depth); vertices.emplace_back(rightX, topY, depth); } data.tlas = makeTopLevelAccelerationStructure(); data.blas = makeBottomLevelAccelerationStructure(); VkGeometryInstanceFlagsKHR instanceFlags = 0u; if (triangles) instanceFlags |= VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR; data.blas->addGeometry(vertices, triangles, VK_GEOMETRY_NO_DUPLICATE_ANY_HIT_INVOCATION_BIT_KHR); data.blas->createAndBuild(vkd, device, cmdBuffer, alloc); de::SharedPtr blasSharedPtr (data.blas.release()); data.tlas->setInstanceCount(1u); data.tlas->addInstance(blasSharedPtr, identityMatrix3x4, 0u, 0xFFu, 0u, instanceFlags); data.tlas->createAndBuild(vkd, device, cmdBuffer, alloc); return data; } const auto kShaderAccess = (VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT); struct Resource { VkDescriptorType descriptorType; ResourceType resourceType; Move sampler; de::MovePtr imageWithMemory; Move imageView; de::MovePtr bufferWithMemory; Move bufferView; AccelerationStructureData asData; deUint32 initialValue; Resource (VkDescriptorType descriptorType_, const DeviceInterface& vkd, VkDevice device, Allocator& alloc, deUint32 qIndex, VkQueue queue, bool useAABBs, deUint32 initialValue_, deUint32 numElements = 1u) : descriptorType (descriptorType_) , resourceType (toResourceType(descriptorType)) , sampler () , imageWithMemory () , imageView () , bufferWithMemory () , bufferView () , asData () , initialValue (initialValue_) { if (numElements != 1u) DE_ASSERT(resourceType == ResourceType::BUFFER); switch (resourceType) { case ResourceType::SAMPLER: sampler = makeDefaultSampler(vkd, device); break; case ResourceType::IMAGE: imageWithMemory = makeDefaultImage(vkd, device, alloc); imageView = makeDefaultImageView(vkd, device, imageWithMemory->get()); break; case ResourceType::COMBINED_IMAGE_SAMPLER: sampler = makeDefaultSampler(vkd, device); imageWithMemory = makeDefaultImage(vkd, device, alloc); imageView = makeDefaultImageView(vkd, device, imageWithMemory->get()); break; case ResourceType::BUFFER: bufferWithMemory = makeDefaultBuffer(vkd, device, alloc, numElements); break; case ResourceType::BUFFER_VIEW: bufferWithMemory = makeDefaultBuffer(vkd, device, alloc); bufferView = makeDefaultBufferView(vkd, device, bufferWithMemory->get()); break; case ResourceType::ACCELERATION_STRUCTURE: { const auto cmdPool = makeCommandPool(vkd, device, qIndex); const auto cmdBufferPtr = allocateCommandBuffer(vkd, device, cmdPool.get(), VK_COMMAND_BUFFER_LEVEL_PRIMARY); const auto cmdBuffer = cmdBufferPtr.get(); const bool triangles = !useAABBs; beginCommandBuffer(vkd, cmdBuffer); asData = makeDefaultAccelerationStructure(vkd, device, cmdBuffer, alloc, triangles, getAccelerationStructureOffsetX(initialValue)); endCommandBuffer(vkd, cmdBuffer); submitCommandsAndWait(vkd, device, queue, cmdBuffer); } break; default: DE_ASSERT(false); break; } if (imageWithMemory || bufferWithMemory) { const auto cmdPool = makeCommandPool(vkd, device, qIndex); const auto cmdBufferPtr = allocateCommandBuffer(vkd, device, cmdPool.get(), VK_COMMAND_BUFFER_LEVEL_PRIMARY); const auto cmdBuffer = cmdBufferPtr.get(); if (imageWithMemory) { // Prepare staging buffer. const auto bufferSize = static_cast(sizeof(initialValue)); const VkBufferUsageFlags bufferUsage = (VK_BUFFER_USAGE_TRANSFER_SRC_BIT); const auto stagingBufferInfo = makeBufferCreateInfo(bufferSize, bufferUsage); BufferWithMemory stagingBuffer(vkd, device, alloc, stagingBufferInfo, MemoryRequirement::HostVisible); auto& bufferAlloc = stagingBuffer.getAllocation(); void* bufferData = bufferAlloc.getHostPtr(); deMemcpy(bufferData, &initialValue, sizeof(initialValue)); flushAlloc(vkd, device, bufferAlloc); beginCommandBuffer(vkd, cmdBuffer); // Transition and copy image. const auto copyRegion = makeBufferImageCopy(makeExtent3D(1u, 1u, 1u), makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u)); // Switch image to TRANSFER_DST_OPTIMAL before copying data to it. const auto subresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u); const auto preTransferBarrier = makeImageMemoryBarrier( 0u, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, imageWithMemory->get(), subresourceRange); vkd.cmdPipelineBarrier( cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 0u, nullptr, 0u, nullptr, 1u, &preTransferBarrier); // Copy data to image. vkd.cmdCopyBufferToImage(cmdBuffer, stagingBuffer.get(), imageWithMemory->get(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1u, ©Region); // Switch image to the GENERAL layout before reading or writing to it from shaders. const auto postTransferBarrier = makeImageMemoryBarrier( VK_ACCESS_TRANSFER_WRITE_BIT, kShaderAccess, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL, imageWithMemory->get(), subresourceRange); vkd.cmdPipelineBarrier( cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0u, 0u, nullptr, 0u, nullptr, 1u, &postTransferBarrier); endCommandBuffer(vkd, cmdBuffer); submitCommandsAndWait(vkd, device, queue, cmdBuffer); } if (bufferWithMemory) { auto& bufferAlloc = bufferWithMemory->getAllocation(); void* bufferData = bufferAlloc.getHostPtr(); const std::vector bufferValues(numElements, initialValue); deMemcpy(bufferData, bufferValues.data(), de::dataSize(bufferValues)); flushAlloc(vkd, device, bufferAlloc); beginCommandBuffer(vkd, cmdBuffer); // Make sure host writes happen before shader reads/writes. Note: this barrier is not needed in theory. const auto hostToShaderBarrier = makeMemoryBarrier(VK_ACCESS_HOST_WRITE_BIT, kShaderAccess); vkd.cmdPipelineBarrier( cmdBuffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0u, 1u, &hostToShaderBarrier, 0u, nullptr, 0u, nullptr); endCommandBuffer(vkd, cmdBuffer); submitCommandsAndWait(vkd, device, queue, cmdBuffer); } } } // Remove problematic copy constructor. Resource (const Resource&) = delete; // Make it movable. Resource (Resource&& other) noexcept : descriptorType (other.descriptorType) , resourceType (other.resourceType) , sampler (other.sampler) , imageWithMemory (other.imageWithMemory.release()) , imageView (other.imageView) , bufferWithMemory (other.bufferWithMemory.release()) , bufferView (other.bufferView) , asData (std::move(other.asData)) , initialValue (other.initialValue) {} ~Resource () {} WriteInfo makeWriteInfo () const { using WriteInfoPtr = de::MovePtr; WriteInfoPtr writeInfo; switch (resourceType) { case ResourceType::SAMPLER: { const VkDescriptorImageInfo imageInfo = { sampler.get(), DE_NULL, VK_IMAGE_LAYOUT_UNDEFINED }; writeInfo = WriteInfoPtr (new WriteInfo(imageInfo)); } break; case ResourceType::IMAGE: { const VkDescriptorImageInfo imageInfo = { DE_NULL, imageView.get(), VK_IMAGE_LAYOUT_GENERAL }; writeInfo = WriteInfoPtr (new WriteInfo(imageInfo)); } break; case ResourceType::COMBINED_IMAGE_SAMPLER: { const VkDescriptorImageInfo imageInfo = { sampler.get(), imageView.get(), VK_IMAGE_LAYOUT_GENERAL }; writeInfo = WriteInfoPtr (new WriteInfo(imageInfo)); } break; case ResourceType::BUFFER: { const VkDescriptorBufferInfo bufferInfo = { bufferWithMemory->get(), 0ull, static_cast(sizeof(deUint32)) }; writeInfo = WriteInfoPtr (new WriteInfo(bufferInfo)); } break; case ResourceType::BUFFER_VIEW: writeInfo = WriteInfoPtr (new WriteInfo(bufferView.get())); break; case ResourceType::ACCELERATION_STRUCTURE: { VkWriteDescriptorSetAccelerationStructureKHR asWrite = initVulkanStructure(); asWrite.accelerationStructureCount = 1u; asWrite.pAccelerationStructures = asData.tlas.get()->getPtr(); writeInfo = WriteInfoPtr (new WriteInfo(asWrite)); } break; default: DE_ASSERT(false); break; } return *writeInfo; } tcu::Maybe getStoredValue (const DeviceInterface& vkd, VkDevice device, Allocator& alloc, deUint32 qIndex, VkQueue queue, deUint32 position = 0u) const { if (position != 0u) DE_ASSERT(static_cast(bufferWithMemory)); if (imageWithMemory || bufferWithMemory) { // Command pool and buffer. const auto cmdPool = makeCommandPool(vkd, device, qIndex); const auto cmdBufferPtr = allocateCommandBuffer(vkd, device, cmdPool.get(), VK_COMMAND_BUFFER_LEVEL_PRIMARY); const auto cmdBuffer = cmdBufferPtr.get(); if (imageWithMemory) { // Prepare staging buffer. deUint32 result; const auto bufferSize = static_cast(sizeof(result)); const VkBufferUsageFlags bufferUsage = (VK_BUFFER_USAGE_TRANSFER_DST_BIT); const auto stagingBufferInfo = makeBufferCreateInfo(bufferSize, bufferUsage); BufferWithMemory stagingBuffer(vkd, device, alloc, stagingBufferInfo, MemoryRequirement::HostVisible); auto& bufferAlloc = stagingBuffer.getAllocation(); void* bufferData = bufferAlloc.getHostPtr(); // Copy image value to staging buffer. beginCommandBuffer(vkd, cmdBuffer); // Make sure shader accesses happen before transfers and prepare image for transfer. const auto colorResourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u); const auto preTransferBarrier = makeImageMemoryBarrier( kShaderAccess, VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, imageWithMemory->get(), colorResourceRange); vkd.cmdPipelineBarrier( cmdBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 0u, nullptr, 0u, nullptr, 1u, &preTransferBarrier); // Copy image contents to staging buffer. const auto copyRegion = makeBufferImageCopy(makeExtent3D(1u, 1u, 1u), makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u)); vkd.cmdCopyImageToBuffer(cmdBuffer, imageWithMemory->get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, stagingBuffer.get(), 1u, ©Region); // Make sure writes are visible from the host. const auto postTransferBarrier = makeMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT); vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 1u, &postTransferBarrier, 0u, nullptr, 0u, nullptr); endCommandBuffer(vkd, cmdBuffer); submitCommandsAndWait(vkd, device, queue, cmdBuffer); // Get value from staging buffer. invalidateAlloc(vkd, device, bufferAlloc); deMemcpy(&result, bufferData, sizeof(result)); return tcu::just(result); } if (bufferWithMemory) { auto& bufferAlloc = bufferWithMemory->getAllocation(); auto bufferData = reinterpret_cast(bufferAlloc.getHostPtr()); deUint32 result; // Make sure shader writes are visible from the host. beginCommandBuffer(vkd, cmdBuffer); const auto shaderToHostBarrier = makeMemoryBarrier(kShaderAccess, VK_ACCESS_HOST_READ_BIT); vkd.cmdPipelineBarrier( cmdBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 1u, &shaderToHostBarrier, 0u, nullptr, 0u, nullptr); endCommandBuffer(vkd, cmdBuffer); submitCommandsAndWait(vkd, device, queue, cmdBuffer); invalidateAlloc(vkd, device, bufferAlloc); deMemcpy(&result, bufferData + sizeof(deUint32) * static_cast(position), sizeof(result)); return tcu::just(result); } } return tcu::Nothing; } }; struct BindingInterface { // Minimum number of iterations to test all mutable types. virtual deUint32 maxTypes () const = 0; // Types that will be used by the binding at a given iteration. virtual std::vector typesAtIteration (deUint32 iteration) const = 0; // Binding's main type. virtual VkDescriptorType mainType () const = 0; // Binding's list of mutable types, if present. virtual std::vector mutableTypes () const = 0; // Descriptor count in the binding. virtual size_t size () const = 0; // Is the binding an array binding? virtual bool isArray () const = 0; // Is the binding an unbounded array? virtual bool isUnbounded () const = 0; // Will the binding use different descriptor types in a given iteration? virtual bool needsAliasing (deUint32 iteration) const { const auto typesVec = typesAtIteration(iteration); std::set descTypes(begin(typesVec), end(typesVec)); return (descTypes.size() > 1u); } // Will the binding need aliasing on any iteration up to a given number? virtual bool needsAliasingUpTo (deUint32 numIterations) const { std::vector needsAliasingFlags; needsAliasingFlags.reserve(numIterations); for (deUint32 iter = 0u; iter < numIterations; ++iter) needsAliasingFlags.push_back(needsAliasing(iter)); return std::any_of(begin(needsAliasingFlags), end(needsAliasingFlags), [] (bool f) { return f; }); } private: virtual bool hasDescriptorType (deUint32 iteration, VkDescriptorType descriptorType) const { const auto typesVec = typesAtIteration(iteration); return (std::find(begin(typesVec), end(typesVec), descriptorType) != end(typesVec)); } public: // Convert one particular binding to a mutable or non-mutable equivalent binding, returning the equivalent binding. virtual de::MovePtr toMutable (deUint32 iteration) const = 0; virtual de::MovePtr toNonMutable (deUint32 iteration) const = 0; // Create resources needed to back up this binding. virtual std::vector createResources ( const DeviceInterface& vkd, VkDevice device, Allocator& alloc, deUint32 qIndex, VkQueue queue, deUint32 iteration, bool useAABBs, deUint32 baseValue) const = 0; // Get GLSL binding declarations. Note: no array size means no array, if size is < 0 it means unbounded array. virtual std::string glslDeclarations (deUint32 iteration, deUint32 setNum, deUint32 bindingNum, deUint32 inputAttachmentIdx, tcu::Maybe arraySize) const = 0; // Get GLSL statements to check this binding. virtual std::string glslCheckStatements (deUint32 iteration, deUint32 setNum, deUint32 bindingNum, deUint32 baseValue, tcu::Maybe arrayIndex, bool usePushConstants) const = 0; }; // Represents a single binding that will be used in a test. class SingleBinding : public BindingInterface { private: VkDescriptorType type; // The descriptor type. std::vector mutableTypesVec; // The types that will be used for each iteration of a test if mutable. public: SingleBinding (VkDescriptorType type_, std::vector mutableTypes_) : type (type_) , mutableTypesVec (std::move(mutableTypes_)) { static const auto kForbiddenMutableTypes = getForbiddenMutableTypes(); const auto kBeginForbidden = begin(kForbiddenMutableTypes); const auto kEndForbidden = end(kForbiddenMutableTypes); // For release builds. DE_UNREF(kBeginForbidden); DE_UNREF(kEndForbidden); if (type != VK_DESCRIPTOR_TYPE_MUTABLE_VALVE) { DE_ASSERT(mutableTypesVec.empty()); } else { DE_ASSERT(!mutableTypesVec.empty()); DE_ASSERT(std::none_of(begin(mutableTypesVec), end(mutableTypesVec), [&kBeginForbidden, &kEndForbidden] (VkDescriptorType t) -> bool { return std::find(kBeginForbidden, kEndForbidden, t) != kEndForbidden; })); } } deUint32 maxTypes () const override { if (type != VK_DESCRIPTOR_TYPE_MUTABLE_VALVE) return 1u; const auto vecSize = mutableTypesVec.size(); DE_ASSERT(vecSize <= std::numeric_limits::max()); return static_cast(vecSize); } VkDescriptorType typeAtIteration (deUint32 iteration) const { return typesAtIteration(iteration)[0]; } std::vector usedTypes () const { if (type != VK_DESCRIPTOR_TYPE_MUTABLE_VALVE) return std::vector(1u, type); return mutableTypesVec; } std::vector typesAtIteration (deUint32 iteration) const override { const auto typesVec = usedTypes(); return std::vector(1u, typesVec[static_cast(iteration) % typesVec.size()]); } VkDescriptorType mainType () const override { return type; } std::vector mutableTypes () const override { return mutableTypesVec; } size_t size () const override { return size_t{1u}; } bool isArray () const override { return false; } bool isUnbounded () const override { return false; } de::MovePtr toMutable (deUint32 iteration) const override { DE_UNREF(iteration); static const auto kMandatoryMutableTypeFlags = toDescriptorTypeFlags(getMandatoryMutableTypes()); if (type == VK_DESCRIPTOR_TYPE_MUTABLE_VALVE) { const auto descFlags = (toDescriptorTypeFlags(mutableTypesVec) | kMandatoryMutableTypeFlags); return de::MovePtr(new SingleBinding(type, toDescriptorTypeVector(descFlags))); } // Make sure it's not a forbidden mutable type. static const auto kForbiddenMutableTypes = getForbiddenMutableTypes(); DE_ASSERT(std::find(begin(kForbiddenMutableTypes), end(kForbiddenMutableTypes), type) == end(kForbiddenMutableTypes)); // Convert the binding to mutable using a wider set of descriptor types if possible, including the binding type. const auto descFlags = (kMandatoryMutableTypeFlags | toDescriptorTypeFlagBit(type)); return de::MovePtr(new SingleBinding(VK_DESCRIPTOR_TYPE_MUTABLE_VALVE, toDescriptorTypeVector(descFlags))); } de::MovePtr toNonMutable (deUint32 iteration) const override { return de::MovePtr(new SingleBinding(typeAtIteration(iteration), std::vector())); } std::vector createResources ( const DeviceInterface& vkd, VkDevice device, Allocator& alloc, deUint32 qIndex, VkQueue queue, deUint32 iteration, bool useAABBs, deUint32 baseValue) const override { const auto descriptorType = typeAtIteration(iteration); std::vector resources; resources.emplace_back(descriptorType, vkd, device, alloc, qIndex, queue, useAABBs, baseValue); return resources; } std::string glslDeclarations (deUint32 iteration, deUint32 setNum, deUint32 bindingNum, deUint32 inputAttachmentIdx, tcu::Maybe arraySize) const override { const auto descriptorType = typeAtIteration(iteration); const std::string arraySuffix = ((static_cast(arraySize)) ? ((arraySize.get() < 0) ? "[]" : ("[" + de::toString(arraySize.get()) + "]")) : ""); const std::string layoutAttribs = "set=" + de::toString(setNum) + ", binding=" + de::toString(bindingNum); const std::string bindingSuffix = "_" + de::toString(setNum) + "_" + de::toString(bindingNum); const std::string nameSuffix = bindingSuffix + arraySuffix; std::ostringstream declarations; declarations << "layout ("; switch (descriptorType) { case VK_DESCRIPTOR_TYPE_SAMPLER: declarations << layoutAttribs << ") uniform sampler sampler" << nameSuffix; break; case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: declarations << layoutAttribs << ") uniform usampler2D combinedSampler" << nameSuffix; break; case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: declarations << layoutAttribs << ") uniform utexture2D sampledImage" << nameSuffix; break; case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: declarations << layoutAttribs << ") uniform uboBlock" << bindingSuffix << " { uint val; } ubo" << nameSuffix; break; case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: declarations << layoutAttribs << ") buffer sboBlock" << bindingSuffix << " { uint val; } ssbo" << nameSuffix; break; case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: declarations << layoutAttribs << ") uniform utextureBuffer uniformTexel" << nameSuffix; break; case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: declarations << layoutAttribs << ", r32ui) uniform uimageBuffer storageTexel" << nameSuffix; break; case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: declarations << layoutAttribs << ", r32ui) uniform uimage2D storageImage" << nameSuffix; break; case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: declarations << layoutAttribs << ", input_attachment_index=" << inputAttachmentIdx << ") uniform usubpassInput inputAttachment" << nameSuffix; break; case VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR: declarations << layoutAttribs << ") uniform accelerationStructureEXT accelerationStructure" << nameSuffix; break; default: DE_ASSERT(false); break; } declarations << ";\n"; return declarations.str(); } std::string glslCheckStatements (deUint32 iteration, deUint32 setNum, deUint32 bindingNum, deUint32 baseValue_, tcu::Maybe arrayIndex, bool usePushConstants) const override { const auto descriptorType = typeAtIteration(iteration); const std::string bindingSuffix = "_" + de::toString(setNum) + "_" + de::toString(bindingNum); std::string indexSuffix; if (arrayIndex) { indexSuffix = de::toString(arrayIndex.get()); if (usePushConstants) indexSuffix += " + pc.zero"; indexSuffix = "[" + indexSuffix + "]"; } const std::string nameSuffix = bindingSuffix + indexSuffix; const std::string baseValue = toHex(baseValue_); const std::string externalImageValue = toHex(getExternalSampledImageValue()); const std::string mask = toHex(getStoredValueMask()); std::ostringstream checks; // Note: all of these depend on an external anyError uint variable. switch (descriptorType) { case VK_DESCRIPTOR_TYPE_SAMPLER: // Note this depends on an "externalSampledImage" binding. checks << " {\n"; checks << " uint readValue = texture(usampler2D(externalSampledImage, sampler" << nameSuffix << "), vec2(0, 0)).r;\n"; checks << " debugPrintfEXT(\"iteration-" << iteration << nameSuffix << ": 0x%xu\\n\", readValue);\n"; checks << " anyError |= ((readValue == " << externalImageValue << ") ? 0u : 1u);\n"; //checks << " anyError = readValue;\n"; checks << " }\n"; break; case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: checks << " {\n"; checks << " uint readValue = texture(combinedSampler" << nameSuffix << ", vec2(0, 0)).r;\n"; checks << " debugPrintfEXT(\"iteration-" << iteration << nameSuffix << ": 0x%xu\\n\", readValue);\n"; checks << " anyError |= ((readValue == " << baseValue << ") ? 0u : 1u);\n"; //checks << " anyError = readValue;\n"; checks << " }\n"; break; case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: // Note this depends on an "externalSampler" binding. checks << " {\n"; checks << " uint readValue = texture(usampler2D(sampledImage" << nameSuffix << ", externalSampler), vec2(0, 0)).r;\n"; checks << " debugPrintfEXT(\"iteration-" << iteration << nameSuffix << ": 0x%xu\\n\", readValue);\n"; checks << " anyError |= ((readValue == " << baseValue << ") ? 0u : 1u);\n"; //checks << " anyError = readValue;\n"; checks << " }\n"; break; case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: checks << " {\n"; checks << " uint readValue = ubo" << nameSuffix << ".val;\n"; checks << " debugPrintfEXT(\"iteration-" << iteration << nameSuffix << ": 0x%xu\\n\", readValue);\n"; checks << " anyError |= ((readValue == " << baseValue << ") ? 0u : 1u);\n"; //checks << " anyError = readValue;\n"; checks << " }\n"; break; case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: checks << " {\n"; checks << " uint readValue = ssbo" << nameSuffix << ".val;\n"; checks << " debugPrintfEXT(\"iteration-" << iteration << nameSuffix << ": 0x%xu\\n\", readValue);\n"; checks << " anyError |= ((readValue == " << baseValue << ") ? 0u : 1u);\n"; //checks << " anyError = readValue;\n"; // Check writes. checks << " ssbo" << nameSuffix << ".val = (readValue | " << mask << ");\n"; checks << " }\n"; break; case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: checks << " {\n"; checks << " uint readValue = texelFetch(uniformTexel" << nameSuffix << ", 0).x;\n"; checks << " debugPrintfEXT(\"iteration-" << iteration << nameSuffix << ": 0x%xu\\n\", readValue);\n"; checks << " anyError |= ((readValue == " << baseValue << ") ? 0u : 1u);\n"; //checks << " anyError = readValue;\n"; checks << " }\n"; break; case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: checks << " {\n"; checks << " uint readValue = imageLoad(storageTexel" << nameSuffix << ", 0).x;\n"; checks << " debugPrintfEXT(\"iteration-" << iteration << nameSuffix << ": 0x%xu\\n\", readValue);\n"; checks << " anyError |= ((readValue == " << baseValue << ") ? 0u : 1u);\n"; //checks << " anyError = readValue;\n"; checks << " readValue |= " << mask << ";\n"; // Check writes. checks << " imageStore(storageTexel" << nameSuffix << ", 0, uvec4(readValue, 0, 0, 0));\n"; checks << " }\n"; break; case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: checks << " {\n"; checks << " uint readValue = imageLoad(storageImage" << nameSuffix << ", ivec2(0, 0)).x;\n"; checks << " debugPrintfEXT(\"iteration-" << iteration << nameSuffix << ": 0x%xu\\n\", readValue);\n"; checks << " anyError |= ((readValue == " << baseValue << ") ? 0u : 1u);\n"; //checks << " anyError = readValue;\n"; checks << " readValue |= " << mask << ";\n"; // Check writes. checks << " imageStore(storageImage" << nameSuffix << ", ivec2(0, 0), uvec4(readValue, 0, 0, 0));\n"; checks << " }\n"; break; case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: checks << " {\n"; checks << " uint readValue = subpassLoad(inputAttachment" << nameSuffix << ").x;\n"; checks << " debugPrintfEXT(\"iteration-" << iteration << nameSuffix << ": 0x%xu\\n\", readValue);\n"; checks << " anyError |= ((readValue == " << baseValue << ") ? 0u : 1u);\n"; //checks << " anyError = readValue;\n"; checks << " }\n"; break; case VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR: checks << " {\n"; checks << " const uint cullMask = 0xFF;\n"; checks << " const vec3 origin = vec3(" << getAccelerationStructureOffsetX(baseValue_) << ".0, 0.0, 0.0);\n"; checks << " const vec3 direction = vec3(0.0, 0.0, 1.0);\n"; checks << " const float tmin = 1.0;\n"; checks << " const float tmax = 10.0;\n"; checks << " uint candidateFound = 0u;\n"; checks << " rayQueryEXT rq;\n"; checks << " rayQueryInitializeEXT(rq, accelerationStructure" << nameSuffix << ", gl_RayFlagsNoneEXT, cullMask, origin, tmin, direction, tmax);\n"; checks << " while (rayQueryProceedEXT(rq)) {\n"; checks << " const uint candidateType = rayQueryGetIntersectionTypeEXT(rq, false);\n"; checks << " if (candidateType == gl_RayQueryCandidateIntersectionTriangleEXT || candidateType == gl_RayQueryCandidateIntersectionAABBEXT) {\n"; checks << " candidateFound = 1u;\n"; checks << " }\n"; checks << " }\n"; checks << " anyError |= ((candidateFound == 1u) ? 0u : 1u);\n"; checks << " }\n"; break; default: DE_ASSERT(false); break; } return checks.str(); } }; // Represents an array of bindings. Individual bindings are stored as SingleBindings because each one of them may take a different // type in each iteration (i.e. they can all have different descriptor type vectors). class ArrayBinding : public BindingInterface { private: bool unbounded; std::vector bindings; public: ArrayBinding (bool unbounded_, std::vector bindings_) : unbounded (unbounded_) , bindings (std::move(bindings_)) { // We need to check all single bindings have the same effective type, even if mutable descriptors have different orders. DE_ASSERT(!bindings.empty()); std::set basicTypes; std::set bindingTypes; for (const auto& b : bindings) { basicTypes.insert(b.mainType()); bindingTypes.insert(toDescriptorTypeFlags(b.usedTypes())); } DE_ASSERT(basicTypes.size() == 1u); DE_ASSERT(bindingTypes.size() == 1u); // For release builds. DE_UNREF(basicTypes); DE_UNREF(bindingTypes); } deUint32 maxTypes () const override { // Each binding may have the same effective type but a different number of iterations due to repeated types. std::vector bindingSizes; bindingSizes.reserve(bindings.size()); std::transform(begin(bindings), end(bindings), std::back_inserter(bindingSizes), [] (const SingleBinding& b) { return b.usedTypes().size(); }); const auto maxElement = std::max_element(begin(bindingSizes), end(bindingSizes)); DE_ASSERT(maxElement != end(bindingSizes)); DE_ASSERT(*maxElement <= std::numeric_limits::max()); return static_cast(*maxElement); } std::vector typesAtIteration (deUint32 iteration) const override { std::vector result; result.reserve(bindings.size()); for (const auto& b : bindings) result.push_back(b.typeAtIteration(iteration)); return result; } VkDescriptorType mainType () const override { return bindings[0].mainType(); } std::vector mutableTypes () const override { return bindings[0].mutableTypes(); } size_t size () const override { return bindings.size(); } bool isArray () const override { return true; } bool isUnbounded () const override { return unbounded; } de::MovePtr toMutable (deUint32 iteration) const override { // Replicate the first binding once converted, as all are equivalent. const auto firstBindingPtr = bindings[0].toMutable(iteration); const auto firstBinding = *dynamic_cast(firstBindingPtr.get()); const std::vector newBindings (bindings.size(), firstBinding); return de::MovePtr(new ArrayBinding(unbounded, newBindings)); } de::MovePtr toNonMutable (deUint32 iteration) const override { // Make sure this binding can be converted to nonmutable for a given iteration. DE_ASSERT(!needsAliasing(iteration)); // We could use each SingleBinding's toNonMutable(), but this is the same. const auto descType = bindings[0].typeAtIteration(iteration); const SingleBinding firstBinding (descType, std::vector()); const std::vector newBindings (bindings.size(), firstBinding); return de::MovePtr(new ArrayBinding(unbounded, newBindings)); } std::vector createResources ( const DeviceInterface& vkd, VkDevice device, Allocator& alloc, deUint32 qIndex, VkQueue queue, deUint32 iteration, bool useAABBs, deUint32 baseValue) const override { std::vector resources; const auto numBindings = static_cast(bindings.size()); for (deUint32 i = 0u; i < numBindings; ++i) { auto resourceVec = bindings[i].createResources(vkd, device, alloc, qIndex, queue, iteration, useAABBs, baseValue + i); resources.emplace_back(std::move(resourceVec[0])); } return resources; } // We will ignore the array size parameter. std::string glslDeclarations (deUint32 iteration, deUint32 setNum, deUint32 bindingNum, deUint32 inputAttachmentIdx, tcu::Maybe arraySize) const override { const auto descriptorCount = bindings.size(); const auto arraySizeVal = (isUnbounded() ? tcu::just(deInt32{-1}) : tcu::just(static_cast(descriptorCount))); DE_UNREF(arraySize); DE_ASSERT(descriptorCount < static_cast(std::numeric_limits::max())); // Maybe a single declaration is enough. if (!needsAliasing(iteration)) return bindings[0].glslDeclarations(iteration, setNum, bindingNum, inputAttachmentIdx, arraySizeVal); // Aliasing needed. Avoid reusing types. const auto descriptorTypes = typesAtIteration(iteration); std::set usedTypes; std::ostringstream declarations; for (size_t descriptorIdx = 0u; descriptorIdx < descriptorCount; ++descriptorIdx) { const auto& descriptorType = descriptorTypes[descriptorIdx]; if (usedTypes.count(descriptorType) > 0) continue; usedTypes.insert(descriptorType); declarations << bindings[descriptorIdx].glslDeclarations(iteration, setNum, bindingNum, inputAttachmentIdx, arraySizeVal); } return declarations.str(); } std::string glslCheckStatements (deUint32 iteration, deUint32 setNum, deUint32 bindingNum, deUint32 baseValue_, tcu::Maybe arrayIndex, bool usePushConstants) const override { DE_ASSERT(!arrayIndex); DE_UNREF(arrayIndex); // For release builds. std::ostringstream checks; const auto numDescriptors = static_cast(bindings.size()); for (deUint32 descriptorIdx = 0u; descriptorIdx < numDescriptors; ++descriptorIdx) { const auto& binding = bindings[descriptorIdx]; checks << binding.glslCheckStatements(iteration, setNum, bindingNum, baseValue_ + descriptorIdx, tcu::just(descriptorIdx), usePushConstants); } return checks.str(); } }; class DescriptorSet; using DescriptorSetPtr = de::SharedPtr; class DescriptorSet { public: using BindingInterfacePtr = de::MovePtr; using BindingPtrVector = std::vector; private: BindingPtrVector bindings; public: explicit DescriptorSet (BindingPtrVector& bindings_) : bindings(std::move(bindings_)) { DE_ASSERT(!bindings.empty()); } size_t numBindings () const { return bindings.size(); } const BindingInterface* getBinding (size_t bindingIdx) const { return bindings.at(bindingIdx).get(); } // Maximum number of descriptor types used by any binding in the set. deUint32 maxTypes () const { std::vector maxSizes; maxSizes.reserve(bindings.size()); std::transform(begin(bindings), end(bindings), std::back_inserter(maxSizes), [] (const BindingInterfacePtr& b) { return b->maxTypes(); }); const auto maxElement = std::max_element(begin(maxSizes), end(maxSizes)); DE_ASSERT(maxElement != end(maxSizes)); return *maxElement; } // Create another descriptor set that can be the source for copies when setting descriptor values. DescriptorSetPtr genSourceSet (SourceSetStrategy strategy, deUint32 iteration) const { BindingPtrVector newBindings; for (const auto& b : bindings) { if (strategy == SourceSetStrategy::MUTABLE) newBindings.push_back(b->toMutable(iteration)); else newBindings.push_back(b->toNonMutable(iteration)); } return DescriptorSetPtr(new DescriptorSet(newBindings)); } // Makes a descriptor pool that can be used when allocating descriptors for this set. Move makeDescriptorPool (const DeviceInterface& vkd, VkDevice device, PoolMutableStrategy strategy, VkDescriptorPoolCreateFlags flags) const { std::vector poolSizes; std::vector> mutableTypesVec; std::vector mutableTypeLists; // Make vector element addresses stable. const auto bindingCount = numBindings(); poolSizes.reserve(bindingCount); mutableTypesVec.reserve(bindingCount); mutableTypeLists.reserve(bindingCount); for (const auto& b : bindings) { const auto mainType = b->mainType(); const VkDescriptorPoolSize poolSize = { mainType, static_cast(b->size()), }; poolSizes.push_back(poolSize); if (strategy == PoolMutableStrategy::KEEP_TYPES || strategy == PoolMutableStrategy::EXPAND_TYPES) { if (mainType == VK_DESCRIPTOR_TYPE_MUTABLE_VALVE) { if (strategy == PoolMutableStrategy::KEEP_TYPES) { mutableTypesVec.emplace_back(b->mutableTypes()); } else { // Expand the type list with the mandatory types. static const auto mandatoryTypesFlags = toDescriptorTypeFlags(getMandatoryMutableTypes()); const auto bindingTypes = toDescriptorTypeVector(mandatoryTypesFlags | toDescriptorTypeFlags(b->mutableTypes())); mutableTypesVec.emplace_back(bindingTypes); } const auto& lastVec = mutableTypesVec.back(); const VkMutableDescriptorTypeListVALVE typeList = { static_cast(lastVec.size()), de::dataOrNull(lastVec) }; mutableTypeLists.push_back(typeList); } else { const VkMutableDescriptorTypeListVALVE typeList = { 0u, nullptr }; mutableTypeLists.push_back(typeList); } } else if (strategy == PoolMutableStrategy::NO_TYPES) ; // Do nothing, we will not use any type list. else DE_ASSERT(false); } VkDescriptorPoolCreateInfo poolCreateInfo = initVulkanStructure(); poolCreateInfo.maxSets = 1u; poolCreateInfo.flags = flags; poolCreateInfo.poolSizeCount = static_cast(poolSizes.size()); poolCreateInfo.pPoolSizes = de::dataOrNull(poolSizes); VkMutableDescriptorTypeCreateInfoVALVE mutableInfo = initVulkanStructure(); if (strategy == PoolMutableStrategy::KEEP_TYPES || strategy == PoolMutableStrategy::EXPAND_TYPES) { mutableInfo.mutableDescriptorTypeListCount = static_cast(mutableTypeLists.size()); mutableInfo.pMutableDescriptorTypeLists = de::dataOrNull(mutableTypeLists); poolCreateInfo.pNext = &mutableInfo; } return createDescriptorPool(vkd, device, &poolCreateInfo); } private: // Building the descriptor set layout create info structure is cumbersome, so we'll reuse the same procedure to check support // and create the layout. This structure contains the result. "supported" is created as an enum to avoid the Move<> to bool // conversion cast in the contructors. struct DescriptorSetLayoutResult { enum class LayoutSupported { NO = 0, YES }; LayoutSupported supported; Move layout; explicit DescriptorSetLayoutResult (Move&& layout_) : supported (LayoutSupported::YES) , layout (layout_) {} explicit DescriptorSetLayoutResult (LayoutSupported supported_) : supported (supported_) , layout () {} }; DescriptorSetLayoutResult makeOrCheckDescriptorSetLayout (bool checkOnly, const DeviceInterface& vkd, VkDevice device, VkShaderStageFlags stageFlags, VkDescriptorSetLayoutCreateFlags createFlags) const { const auto numIterations = maxTypes(); std::vector bindingsVec; std::vector> mutableTypesVec; std::vector mutableTypeLists; // Make vector element addresses stable. const auto bindingCount = numBindings(); bindingsVec.reserve(bindingCount); mutableTypesVec.reserve(bindingCount); mutableTypeLists.reserve(bindingCount); for (size_t bindingIdx = 0u; bindingIdx < bindings.size(); ++bindingIdx) { const auto& binding = bindings[bindingIdx]; const auto mainType = binding->mainType(); const VkDescriptorSetLayoutBinding layoutBinding = { static_cast(bindingIdx), // deUint32 binding; mainType, // VkDescriptorType descriptorType; static_cast(binding->size()), // deUint32 descriptorCount; stageFlags, // VkShaderStageFlags stageFlags; nullptr, // const VkSampler* pImmutableSamplers; }; bindingsVec.push_back(layoutBinding); // This list may be empty for non-mutable types, which is fine. mutableTypesVec.push_back(binding->mutableTypes()); const auto& lastVec = mutableTypesVec.back(); const VkMutableDescriptorTypeListVALVE typeList = { static_cast(lastVec.size()), // deUint32 descriptorTypeCount; de::dataOrNull(lastVec), // const VkDescriptorType* pDescriptorTypes; }; mutableTypeLists.push_back(typeList); } // Make sure to include the variable descriptor count and/or update after bind binding flags. const bool updateAfterBind = ((createFlags & VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT) != 0u); bool lastIsUnbounded = false; bool aliasingNeded = false; std::vector bindingNeedsAliasing(bindings.size(), false); for (size_t bindingIdx = 0; bindingIdx < bindings.size(); ++bindingIdx) { if (bindingIdx < bindings.size() - 1) DE_ASSERT(!bindings[bindingIdx]->isUnbounded()); else lastIsUnbounded = bindings[bindingIdx]->isUnbounded(); if (bindings[bindingIdx]->needsAliasingUpTo(numIterations)) { bindingNeedsAliasing[bindingIdx] = true; aliasingNeded = true; } } using FlagsCreateInfoPtr = de::MovePtr; using BindingFlagsVecPtr = de::MovePtr>; FlagsCreateInfoPtr flagsCreateInfo; BindingFlagsVecPtr bindingFlagsVec; if (updateAfterBind || lastIsUnbounded || aliasingNeded) { flagsCreateInfo = FlagsCreateInfoPtr(new VkDescriptorSetLayoutBindingFlagsCreateInfo); *flagsCreateInfo = initVulkanStructure(); bindingFlagsVec = BindingFlagsVecPtr(new std::vector(bindingsVec.size(), (updateAfterBind ? VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT : 0))); if (lastIsUnbounded) bindingFlagsVec->back() |= VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT; for (size_t bindingIdx = 0; bindingIdx < bindings.size(); ++bindingIdx) { if (bindingNeedsAliasing[bindingIdx]) bindingFlagsVec->at(bindingIdx) |= VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT; } flagsCreateInfo->bindingCount = static_cast(bindingFlagsVec->size()); flagsCreateInfo->pBindingFlags = de::dataOrNull(*bindingFlagsVec); } const VkMutableDescriptorTypeCreateInfoVALVE createInfoValve = { VK_STRUCTURE_TYPE_MUTABLE_DESCRIPTOR_TYPE_CREATE_INFO_VALVE, flagsCreateInfo.get(), static_cast(mutableTypeLists.size()), de::dataOrNull(mutableTypeLists), }; const VkDescriptorSetLayoutCreateInfo layoutCreateInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, // VkStructureType sType; &createInfoValve, // const void* pNext; createFlags, // VkDescriptorSetLayoutCreateFlags flags; static_cast(bindingsVec.size()), // deUint32 bindingCount; de::dataOrNull(bindingsVec), // const VkDescriptorSetLayoutBinding* pBindings; }; if (checkOnly) { VkDescriptorSetLayoutSupport support = initVulkanStructure(); vkd.getDescriptorSetLayoutSupport(device, &layoutCreateInfo, &support); DescriptorSetLayoutResult result((support.supported == VK_TRUE) ? DescriptorSetLayoutResult::LayoutSupported::YES : DescriptorSetLayoutResult::LayoutSupported::NO); return result; } else { DescriptorSetLayoutResult result(createDescriptorSetLayout(vkd, device, &layoutCreateInfo)); return result; } } public: Move makeDescriptorSetLayout (const DeviceInterface& vkd, VkDevice device, VkShaderStageFlags stageFlags, VkDescriptorSetLayoutCreateFlags createFlags) const { return makeOrCheckDescriptorSetLayout(false /*checkOnly*/, vkd, device, stageFlags, createFlags).layout; } bool checkDescriptorSetLayout (const DeviceInterface& vkd, VkDevice device, VkShaderStageFlags stageFlags, VkDescriptorSetLayoutCreateFlags createFlags) const { return (makeOrCheckDescriptorSetLayout(true /*checkOnly*/, vkd, device, stageFlags, createFlags).supported == DescriptorSetLayoutResult::LayoutSupported::YES); } size_t numDescriptors () const { size_t total = 0; for (const auto& b : bindings) total += b->size(); return total; } std::vector createResources (const DeviceInterface& vkd, VkDevice device, Allocator& alloc, deUint32 qIndex, VkQueue queue, deUint32 iteration, bool useAABBs) const { // Create resources for each binding. std::vector result; result.reserve(numDescriptors()); const auto bindingsCount = static_cast(bindings.size()); for (deUint32 bindingIdx = 0u; bindingIdx < bindingsCount; ++bindingIdx) { const auto& binding = bindings[bindingIdx]; auto bindingResources = binding->createResources(vkd, device, alloc, qIndex, queue, iteration, useAABBs, getDescriptorNumericValue(iteration, bindingIdx)); for (auto& resource : bindingResources) result.emplace_back(std::move(resource)); } return result; } // Updates a descriptor set with the given resources. Note: the set must have been created with a layout that's compatible with this object. void updateDescriptorSet (const DeviceInterface& vkd, VkDevice device, VkDescriptorSet set, deUint32 iteration, const std::vector& resources) const { // Make sure the number of resources is correct. const auto numResources = resources.size(); DE_ASSERT(numDescriptors() == numResources); std::vector descriptorWrites; descriptorWrites.reserve(numResources); std::vector imageInfoVec; std::vector bufferInfoVec; std::vector bufferViewVec; std::vector asWriteVec; size_t resourceIdx = 0; // We'll be storing pointers to elements of these vectors as we're appending elements, so we need their addresses to be stable. imageInfoVec.reserve(numResources); bufferInfoVec.reserve(numResources); bufferViewVec.reserve(numResources); asWriteVec.reserve(numResources); for (size_t bindingIdx = 0; bindingIdx < bindings.size(); ++bindingIdx) { const auto& binding = bindings[bindingIdx]; const auto descriptorTypes = binding->typesAtIteration(iteration); for (size_t descriptorIdx = 0; descriptorIdx < binding->size(); ++descriptorIdx) { // Make sure the resource type matches the expected value. const auto& resource = resources[resourceIdx]; const auto& descriptorType = descriptorTypes[descriptorIdx]; DE_ASSERT(resource.descriptorType == descriptorType); // Obtain the descriptor write info for the resource. const auto writeInfo = resource.makeWriteInfo(); switch (writeInfo.writeType) { case WriteType::IMAGE_INFO: imageInfoVec.push_back(writeInfo.imageInfo); break; case WriteType::BUFFER_INFO: bufferInfoVec.push_back(writeInfo.bufferInfo); break; case WriteType::BUFFER_VIEW: bufferViewVec.push_back(writeInfo.bufferView); break; case WriteType::ACCELERATION_STRUCTURE_INFO: asWriteVec.push_back(writeInfo.asInfo); break; default: DE_ASSERT(false); break; } // Add a new VkWriteDescriptorSet struct or extend the last one with more info. This helps us exercise different implementation code paths. bool extended = false; if (!descriptorWrites.empty() && descriptorIdx > 0) { auto& last = descriptorWrites.back(); if (last.dstSet == set /* this should always be true */ && last.dstBinding == bindingIdx && (last.dstArrayElement + last.descriptorCount) == descriptorIdx && last.descriptorType == descriptorType && writeInfo.writeType != WriteType::ACCELERATION_STRUCTURE_INFO) { // The new write should be in the same vector (imageInfoVec, bufferInfoVec or bufferViewVec) so increasing the count works. ++last.descriptorCount; extended = true; } } if (!extended) { const VkWriteDescriptorSet write = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, ((writeInfo.writeType == WriteType::ACCELERATION_STRUCTURE_INFO) ? &asWriteVec.back() : nullptr), set, static_cast(bindingIdx), static_cast(descriptorIdx), 1u, descriptorType, (writeInfo.writeType == WriteType::IMAGE_INFO ? &imageInfoVec.back() : nullptr), (writeInfo.writeType == WriteType::BUFFER_INFO ? &bufferInfoVec.back() : nullptr), (writeInfo.writeType == WriteType::BUFFER_VIEW ? &bufferViewVec.back() : nullptr), }; descriptorWrites.push_back(write); } ++resourceIdx; } } // Finally, update descriptor set with all the writes. vkd.updateDescriptorSets(device, static_cast(descriptorWrites.size()), de::dataOrNull(descriptorWrites), 0u, nullptr); } // Copies between descriptor sets. They must be compatible and related to this set. void copyDescriptorSet (const DeviceInterface& vkd, VkDevice device, VkDescriptorSet srcSet, VkDescriptorSet dstSet) const { std::vector copies; for (size_t bindingIdx = 0; bindingIdx < numBindings(); ++bindingIdx) { const auto& binding = getBinding(bindingIdx); const auto bindingNumber = static_cast(bindingIdx); const auto descriptorCount = static_cast(binding->size()); const VkCopyDescriptorSet copy = { VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET, nullptr, // set, binding, array element. srcSet, bindingNumber, 0u, dstSet, bindingNumber, 0u, descriptorCount, }; copies.push_back(copy); } vkd.updateDescriptorSets(device, 0u, nullptr, static_cast(copies.size()), de::dataOrNull(copies)); } // Does any binding in the set need aliasing in a given iteration? bool needsAliasing (deUint32 iteration) const { std::vector aliasingNeededFlags; aliasingNeededFlags.reserve(bindings.size()); std::transform(begin(bindings), end(bindings), std::back_inserter(aliasingNeededFlags), [iteration] (const BindingInterfacePtr& b) { return b->needsAliasing(iteration); }); return std::any_of(begin(aliasingNeededFlags), end(aliasingNeededFlags), [] (bool f) { return f; }); } // Does any binding in the set need aliasing in any iteration? bool needsAnyAliasing () const { const auto numIterations = maxTypes(); std::vector aliasingNeededFlags (numIterations, false); for (deUint32 iteration = 0; iteration < numIterations; ++iteration) aliasingNeededFlags[iteration] = needsAliasing(iteration); return std::any_of(begin(aliasingNeededFlags), end(aliasingNeededFlags), [] (bool f) { return f; }); } // Is the last binding an unbounded array? bool lastBindingIsUnbounded () const { if (bindings.empty()) return false; return bindings.back()->isUnbounded(); } // Get the variable descriptor count for the last binding if any. tcu::Maybe getVariableDescriptorCount () const { if (lastBindingIsUnbounded()) return tcu::just(static_cast(bindings.back()->size())); return tcu::Nothing; } // Check if the set contains a descriptor type of the given type at the given iteration. bool containsTypeAtIteration (VkDescriptorType descriptorType, deUint32 iteration) const { return std::any_of(begin(bindings), end(bindings), [descriptorType, iteration] (const BindingInterfacePtr& b) { const auto types = b->typesAtIteration(iteration); return de::contains(begin(types), end(types), descriptorType); }); } // Is any binding an array? bool hasArrays () const { return std::any_of(begin(bindings), end(bindings), [] (const BindingInterfacePtr& b) { return b->isArray(); }); } }; enum class UpdateType { WRITE = 0, COPY, }; enum class SourceSetType { NORMAL = 0, HOST_ONLY, NO_SOURCE, }; enum class UpdateMoment { NORMAL = 0, UPDATE_AFTER_BIND, }; enum class TestingStage { COMPUTE = 0, VERTEX, TESS_EVAL, TESS_CONTROL, GEOMETRY, FRAGMENT, RAY_GEN, INTERSECTION, ANY_HIT, CLOSEST_HIT, MISS, CALLABLE, }; enum class ArrayAccessType { CONSTANT = 0, PUSH_CONSTANT, NO_ARRAY, }; // Are we testing a ray tracing pipeline stage? bool isRayTracingStage (TestingStage stage) { switch (stage) { case TestingStage::RAY_GEN: case TestingStage::INTERSECTION: case TestingStage::ANY_HIT: case TestingStage::CLOSEST_HIT: case TestingStage::MISS: case TestingStage::CALLABLE: return true; default: break; } return false; } struct TestParams { DescriptorSetPtr descriptorSet; UpdateType updateType; SourceSetStrategy sourceSetStrategy; SourceSetType sourceSetType; PoolMutableStrategy poolMutableStrategy; UpdateMoment updateMoment; ArrayAccessType arrayAccessType; TestingStage testingStage; VkShaderStageFlags getStageFlags () const { VkShaderStageFlags flags = 0u; switch (testingStage) { case TestingStage::COMPUTE: flags |= VK_SHADER_STAGE_COMPUTE_BIT; break; case TestingStage::VERTEX: flags |= VK_SHADER_STAGE_VERTEX_BIT; break; case TestingStage::TESS_EVAL: flags |= VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT; break; case TestingStage::TESS_CONTROL: flags |= VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT; break; case TestingStage::GEOMETRY: flags |= VK_SHADER_STAGE_GEOMETRY_BIT; break; case TestingStage::FRAGMENT: flags |= VK_SHADER_STAGE_FRAGMENT_BIT; break; case TestingStage::RAY_GEN: flags |= VK_SHADER_STAGE_RAYGEN_BIT_KHR; break; case TestingStage::INTERSECTION: flags |= VK_SHADER_STAGE_INTERSECTION_BIT_KHR; break; case TestingStage::ANY_HIT: flags |= VK_SHADER_STAGE_ANY_HIT_BIT_KHR; break; case TestingStage::CLOSEST_HIT: flags |= VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR; break; case TestingStage::MISS: flags |= VK_SHADER_STAGE_MISS_BIT_KHR; break; case TestingStage::CALLABLE: flags |= VK_SHADER_STAGE_CALLABLE_BIT_KHR; break; default: DE_ASSERT(false); break; } return flags; } VkPipelineStageFlags getPipelineWriteStage () const { VkPipelineStageFlags flags = 0u; switch (testingStage) { case TestingStage::COMPUTE: flags |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; break; case TestingStage::VERTEX: flags |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT; break; case TestingStage::TESS_EVAL: flags |= VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT; break; case TestingStage::TESS_CONTROL: flags |= VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT; break; case TestingStage::GEOMETRY: flags |= VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT; break; case TestingStage::FRAGMENT: flags |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; break; case TestingStage::RAY_GEN: // fallthrough case TestingStage::INTERSECTION: // fallthrough case TestingStage::ANY_HIT: // fallthrough case TestingStage::CLOSEST_HIT: // fallthrough case TestingStage::MISS: // fallthrough case TestingStage::CALLABLE: flags |= VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR; break; default: DE_ASSERT(false); break; } return flags; } private: VkDescriptorSetLayoutCreateFlags getLayoutCreateFlags (bool isSourceSet) const { // UPDATE_AFTER_BIND cannot be used with HOST_ONLY sets. //DE_ASSERT(!(updateMoment == UpdateMoment::UPDATE_AFTER_BIND && sourceSetType == SourceSetType::HOST_ONLY)); VkDescriptorSetLayoutCreateFlags createFlags = 0u; if ((!isSourceSet || sourceSetType != SourceSetType::HOST_ONLY) && updateMoment == UpdateMoment::UPDATE_AFTER_BIND) createFlags |= VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT; if (isSourceSet && sourceSetType == SourceSetType::HOST_ONLY) createFlags |= VK_DESCRIPTOR_SET_LAYOUT_CREATE_HOST_ONLY_POOL_BIT_VALVE; return createFlags; } public: VkDescriptorSetLayoutCreateFlags getSrcLayoutCreateFlags () const { return getLayoutCreateFlags(true); } VkDescriptorSetLayoutCreateFlags getDstLayoutCreateFlags () const { return getLayoutCreateFlags(false); } private: VkDescriptorPoolCreateFlags getPoolCreateFlags (bool isSourceSet) const { // UPDATE_AFTER_BIND cannot be used with HOST_ONLY sets. //DE_ASSERT(!(updateMoment == UpdateMoment::UPDATE_AFTER_BIND && sourceSetType == SourceSetType::HOST_ONLY)); VkDescriptorPoolCreateFlags poolCreateFlags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; if ((!isSourceSet || sourceSetType != SourceSetType::HOST_ONLY) && updateMoment == UpdateMoment::UPDATE_AFTER_BIND) poolCreateFlags |= VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT; if (isSourceSet && sourceSetType == SourceSetType::HOST_ONLY) poolCreateFlags |= VK_DESCRIPTOR_POOL_CREATE_HOST_ONLY_BIT_VALVE; return poolCreateFlags; } public: VkDescriptorPoolCreateFlags getSrcPoolCreateFlags () const { return getPoolCreateFlags(true); } VkDescriptorPoolCreateFlags getDstPoolCreateFlags () const { return getPoolCreateFlags(false); } VkPipelineBindPoint getBindPoint () const { if (testingStage == TestingStage::COMPUTE) return VK_PIPELINE_BIND_POINT_COMPUTE; if (isRayTracingStage(testingStage)) return VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR; return VK_PIPELINE_BIND_POINT_GRAPHICS; } }; class MutableTypesTest : public TestCase { public: MutableTypesTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TestParams& params) : TestCase(testCtx, name, description) , m_params(params) {} ~MutableTypesTest () override = default; void initPrograms (vk::SourceCollections& programCollection) const override; TestInstance* createInstance (Context& context) const override; void checkSupport (Context& context) const override; private: TestParams m_params; }; class MutableTypesInstance : public TestInstance { public: MutableTypesInstance (Context& context, const TestParams& params) : TestInstance (context) , m_params (params) {} ~MutableTypesInstance () override = default; tcu::TestStatus iterate () override; private: TestParams m_params; }; // Check if a descriptor set contains a given descriptor type in any iteration up to maxTypes(). bool containsAnyDescriptorType (const DescriptorSet& descriptorSet, VkDescriptorType descriptorType) { const auto numIterations = descriptorSet.maxTypes(); for (deUint32 iter = 0u; iter < numIterations; ++iter) { if (descriptorSet.containsTypeAtIteration(descriptorType, iter)) return true; } return false; } // Check if testing this descriptor set needs an external image (for sampler descriptors). bool needsExternalImage (const DescriptorSet& descriptorSet) { return containsAnyDescriptorType(descriptorSet, VK_DESCRIPTOR_TYPE_SAMPLER); } // Check if testing this descriptor set needs an external sampler (for sampled images). bool needsExternalSampler (const DescriptorSet& descriptorSet) { return containsAnyDescriptorType(descriptorSet, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE); } // Check if this descriptor set contains a input attachments. bool usesInputAttachments (const DescriptorSet& descriptorSet) { return containsAnyDescriptorType(descriptorSet, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT); } // Check if this descriptor set contains acceleration structures. bool usesAccelerationStructures (const DescriptorSet& descriptorSet) { return containsAnyDescriptorType(descriptorSet, VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR); } std::string shaderName (deUint32 iteration) { return ("iteration-" + de::toString(iteration)); } void MutableTypesTest::initPrograms (vk::SourceCollections& programCollection) const { const bool usePushConstants = (m_params.arrayAccessType == ArrayAccessType::PUSH_CONSTANT); const bool useExternalImage = needsExternalImage(*m_params.descriptorSet); const bool useExternalSampler = needsExternalSampler(*m_params.descriptorSet); const bool rayQueries = usesAccelerationStructures(*m_params.descriptorSet); const bool rayTracing = isRayTracingStage(m_params.testingStage); const auto numIterations = m_params.descriptorSet->maxTypes(); const auto numBindings = m_params.descriptorSet->numBindings(); const vk::ShaderBuildOptions rtBuildOptions (programCollection.usedVulkanVersion, vk::SPIRV_VERSION_1_4, 0u, true); // Extra set and bindings for external resources. std::ostringstream extraSet; deUint32 extraBindings = 0u; extraSet << "layout (set=1, binding=" << extraBindings++ << ") buffer OutputBufferBlock { uint value[" << numIterations << "]; } outputBuffer;\n"; if (useExternalImage) extraSet << "layout (set=1, binding=" << extraBindings++ << ") uniform utexture2D externalSampledImage;\n"; if (useExternalSampler) extraSet << "layout (set=1, binding=" << extraBindings++ << ") uniform sampler externalSampler;\n"; // The extra binding below will be declared in the "passthrough" ray generation shader. #if 0 if (rayTracing) extraSet << "layout (set=1, binding=" << extraBindings++ << ") uniform accelerationStructureEXT externalAS;\n"; #endif // Common vertex preamble. std::ostringstream vertexPreamble; vertexPreamble << "vec2 vertexPositions[3] = vec2[](\n" << " vec2(0.0, -0.5),\n" << " vec2(0.5, 0.5),\n" << " vec2(-0.5, 0.5)\n" << ");\n" ; // Vertex shader body common statements. std::ostringstream vertexBodyCommon; vertexBodyCommon << " gl_Position = vec4(vertexPositions[gl_VertexIndex], 0.0, 1.0);\n"; // Common tessellation control preamble. std::ostringstream tescPreamble; tescPreamble << "layout (vertices=3) out;\n" << "in gl_PerVertex\n" << "{\n" << " vec4 gl_Position;\n" << "} gl_in[gl_MaxPatchVertices];\n" << "out gl_PerVertex\n" << "{\n" << " vec4 gl_Position;\n" << "} gl_out[];\n" ; // Common tessellation control body. std::ostringstream tescBodyCommon; tescBodyCommon << " gl_TessLevelInner[0] = 1.0;\n" << " gl_TessLevelInner[1] = 1.0;\n" << " gl_TessLevelOuter[0] = 1.0;\n" << " gl_TessLevelOuter[1] = 1.0;\n" << " gl_TessLevelOuter[2] = 1.0;\n" << " gl_TessLevelOuter[3] = 1.0;\n" << " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n" ; // Common tessellation evaluation preamble. std::ostringstream tesePreamble; tesePreamble << "layout (triangles, fractional_odd_spacing, cw) in;\n" << "in gl_PerVertex\n" << "{\n" << " vec4 gl_Position;\n" << "} gl_in[gl_MaxPatchVertices];\n" << "out gl_PerVertex\n" << "{\n" << " vec4 gl_Position;\n" << "};\n" ; // Common tessellation evaluation body. std::ostringstream teseBodyCommon; teseBodyCommon << " gl_Position = (gl_TessCoord.x * gl_in[0].gl_Position) +\n" << " (gl_TessCoord.y * gl_in[1].gl_Position) +\n" << " (gl_TessCoord.z * gl_in[2].gl_Position);\n" ; // Shader preamble. std::ostringstream preamble; preamble << "#version 460\n" << "#extension GL_EXT_nonuniform_qualifier : enable\n" << "#extension GL_EXT_debug_printf : enable\n" << (rayTracing ? "#extension GL_EXT_ray_tracing : enable\n" : "") << (rayQueries ? "#extension GL_EXT_ray_query : enable\n" : "") << "\n" ; if (m_params.testingStage == TestingStage::VERTEX) { preamble << vertexPreamble.str(); } else if (m_params.testingStage == TestingStage::COMPUTE) { preamble << "layout (local_size_x=1, local_size_y=1, local_size_z=1) in;\n" << "\n" ; } else if (m_params.testingStage == TestingStage::GEOMETRY) { preamble << "layout (triangles) in;\n" << "layout (triangle_strip, max_vertices=3) out;\n" << "in gl_PerVertex\n" << "{\n" << " vec4 gl_Position;\n" << "} gl_in[3];\n" << "out gl_PerVertex\n" << "{\n" << " vec4 gl_Position;\n" << "};\n" ; } else if (m_params.testingStage == TestingStage::TESS_CONTROL) { preamble << tescPreamble.str(); } else if (m_params.testingStage == TestingStage::TESS_EVAL) { preamble << tesePreamble.str(); } else if (m_params.testingStage == TestingStage::CALLABLE) { preamble << "layout (location=0) callableDataInEXT float unusedCallableData;\n"; } else if (m_params.testingStage == TestingStage::CLOSEST_HIT || m_params.testingStage == TestingStage::ANY_HIT || m_params.testingStage == TestingStage::MISS) { preamble << "layout (location=0) rayPayloadInEXT float unusedRayPayload;\n"; } else if (m_params.testingStage == TestingStage::INTERSECTION) { preamble << "hitAttributeEXT vec3 hitAttribute;\n"; } preamble << extraSet.str(); if (usePushConstants) preamble << "layout (push_constant, std430) uniform PushConstantBlock { uint zero; } pc;\n"; preamble << "\n"; // We need to create a shader per iteration. for (deUint32 iter = 0u; iter < numIterations; ++iter) { // Shader preamble. std::ostringstream shader; shader << preamble.str(); deUint32 inputAttachmentCount = 0u; // Descriptor declarations for this iteration. for (size_t bindingIdx = 0; bindingIdx < numBindings; ++bindingIdx) { DE_ASSERT(bindingIdx <= std::numeric_limits::max()); const auto binding = m_params.descriptorSet->getBinding(bindingIdx); const auto bindingTypes = binding->typesAtIteration(iter); const auto hasInputAttachment = de::contains(begin(bindingTypes), end(bindingTypes), VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT); const auto isArray = binding->isArray(); const auto isUnbounded = binding->isUnbounded(); const auto bindingSize = binding->size(); // If the binding is an input attachment, make sure it's not an array. DE_ASSERT(!hasInputAttachment || !isArray); // Make sure the descriptor count fits a deInt32 if needed. DE_ASSERT(!isArray || isUnbounded || bindingSize <= static_cast(std::numeric_limits::max())); const auto arraySize = (isArray ? (isUnbounded ? tcu::just(deInt32{-1}) : tcu::just(static_cast(bindingSize))) : tcu::Nothing); shader << binding->glslDeclarations(iter, 0u, static_cast(bindingIdx), inputAttachmentCount, arraySize); if (hasInputAttachment) ++inputAttachmentCount; } // Main body. shader << "\n" << "void main() {\n" // This checks if we are the first invocation to arrive here, so the checks are executed only once. << " const uint flag = atomicCompSwap(outputBuffer.value[" << iter << "], 0u, 1u);\n" << " if (flag == 0u) {\n" << " uint anyError = 0u;\n" ; for (size_t bindingIdx = 0; bindingIdx < numBindings; ++bindingIdx) { const auto binding = m_params.descriptorSet->getBinding(bindingIdx); const auto idx32 = static_cast(bindingIdx); shader << binding->glslCheckStatements(iter, 0u, idx32, getDescriptorNumericValue(iter, idx32), tcu::Nothing, usePushConstants); } shader << " if (anyError == 0u) {\n" << " atomicAdd(outputBuffer.value[" << iter << "], 1u);\n" << " }\n" << " }\n" // Closes if (flag == 0u). ; if (m_params.testingStage == TestingStage::VERTEX) { shader << vertexBodyCommon.str(); } else if (m_params.testingStage == TestingStage::GEOMETRY) { shader << " gl_Position = gl_in[0].gl_Position; EmitVertex();\n" << " gl_Position = gl_in[1].gl_Position; EmitVertex();\n" << " gl_Position = gl_in[2].gl_Position; EmitVertex();\n" ; } else if (m_params.testingStage == TestingStage::TESS_CONTROL) { shader << tescBodyCommon.str(); } else if (m_params.testingStage == TestingStage::TESS_EVAL) { shader << teseBodyCommon.str(); } shader << "}\n" // End of main(). ; { const auto shaderNameStr = shaderName(iter); const auto shaderStr = shader.str(); auto& glslSource = programCollection.glslSources.add(shaderNameStr); if (m_params.testingStage == TestingStage::COMPUTE) glslSource << glu::ComputeSource(shaderStr); else if (m_params.testingStage == TestingStage::VERTEX) glslSource << glu::VertexSource(shaderStr); else if (m_params.testingStage == TestingStage::FRAGMENT) glslSource << glu::FragmentSource(shaderStr); else if (m_params.testingStage == TestingStage::GEOMETRY) glslSource << glu::GeometrySource(shaderStr); else if (m_params.testingStage == TestingStage::TESS_CONTROL) glslSource << glu::TessellationControlSource(shaderStr); else if (m_params.testingStage == TestingStage::TESS_EVAL) glslSource << glu::TessellationEvaluationSource(shaderStr); else if (m_params.testingStage == TestingStage::RAY_GEN) glslSource << glu::RaygenSource(updateRayTracingGLSL(shaderStr)); else if (m_params.testingStage == TestingStage::INTERSECTION) glslSource << glu::IntersectionSource(updateRayTracingGLSL(shaderStr)); else if (m_params.testingStage == TestingStage::ANY_HIT) glslSource << glu::AnyHitSource(updateRayTracingGLSL(shaderStr)); else if (m_params.testingStage == TestingStage::CLOSEST_HIT) glslSource << glu::ClosestHitSource(updateRayTracingGLSL(shaderStr)); else if (m_params.testingStage == TestingStage::MISS) glslSource << glu::MissSource(updateRayTracingGLSL(shaderStr)); else if (m_params.testingStage == TestingStage::CALLABLE) glslSource << glu::CallableSource(updateRayTracingGLSL(shaderStr)); else DE_ASSERT(false); if (rayTracing || rayQueries) glslSource << rtBuildOptions; } } if (m_params.testingStage == TestingStage::FRAGMENT || m_params.testingStage == TestingStage::GEOMETRY || m_params.testingStage == TestingStage::TESS_CONTROL || m_params.testingStage == TestingStage::TESS_EVAL) { // Add passthrough vertex shader that works for points. std::ostringstream vertPassthrough; vertPassthrough << "#version 460\n" << "out gl_PerVertex\n" << "{\n" << " vec4 gl_Position;\n" << "};\n" << vertexPreamble.str() << "void main() {\n" << vertexBodyCommon.str() << "}\n" ; programCollection.glslSources.add("vert") << glu::VertexSource(vertPassthrough.str()); } if (m_params.testingStage == TestingStage::TESS_CONTROL) { // Add passthrough tessellation evaluation shader. std::ostringstream tesePassthrough; tesePassthrough << "#version 460\n" << tesePreamble.str() << "void main (void)\n" << "{\n" << teseBodyCommon.str() << "}\n" ; programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(tesePassthrough.str()); } if (m_params.testingStage == TestingStage::TESS_EVAL) { // Add passthrough tessellation control shader. std::ostringstream tescPassthrough; tescPassthrough << "#version 460\n" << tescPreamble.str() << "void main (void)\n" << "{\n" << tescBodyCommon.str() << "}\n" ; programCollection.glslSources.add("tesc") << glu::TessellationControlSource(tescPassthrough.str()); } if (rayTracing && m_params.testingStage != TestingStage::RAY_GEN) { // Add a "passthrough" ray generation shader. std::ostringstream rgen; rgen << "#version 460 core\n" << "#extension GL_EXT_ray_tracing : require\n" << "layout (set=1, binding=" << extraBindings << ") uniform accelerationStructureEXT externalAS;\n" << ((m_params.testingStage == TestingStage::CALLABLE) ? "layout (location=0) callableDataEXT float unusedCallableData;\n" : "layout (location=0) rayPayloadEXT float unusedRayPayload;\n") << "\n" << "void main()\n" << "{\n" ; if (m_params.testingStage == TestingStage::INTERSECTION || m_params.testingStage == TestingStage::ANY_HIT || m_params.testingStage == TestingStage::CLOSEST_HIT || m_params.testingStage == TestingStage::MISS) { // We need to trace rays in this case to get hits or misses. const auto zDir = ((m_params.testingStage == TestingStage::MISS) ? "-1.0" : "1.0"); rgen << " const uint cullMask = 0xFF;\n" << " const float tMin = 1.0;\n" << " const float tMax = 10.0;\n" << " const vec3 origin = vec3(0.0, 0.0, 0.0);\n" << " const vec3 direction = vec3(0.0, 0.0, " << zDir << ");\n" << " traceRayEXT(externalAS, gl_RayFlagsNoneEXT, cullMask, 0, 0, 0, origin, tMin, direction, tMax, 0);\n" ; } else if (m_params.testingStage == TestingStage::CALLABLE) { rgen << " executeCallableEXT(0, 0);\n"; } // End of main(). rgen << "}\n"; programCollection.glslSources.add("rgen") << glu::RaygenSource(updateRayTracingGLSL(rgen.str())) << rtBuildOptions; // Intersection shaders will ignore the intersection, so we need a passthrough miss shader. if (m_params.testingStage == TestingStage::INTERSECTION) { std::ostringstream miss; miss << "#version 460 core\n" << "#extension GL_EXT_ray_tracing : require\n" << "layout (location=0) rayPayloadEXT float unusedRayPayload;\n" << "\n" << "void main()\n" << "{\n" << "}\n" ; programCollection.glslSources.add("miss") << glu::MissSource(updateRayTracingGLSL(miss.str())) << rtBuildOptions; } } } TestInstance* MutableTypesTest::createInstance (Context& context) const { return new MutableTypesInstance(context, m_params); } void requirePartiallyBound (Context& context) { context.requireDeviceFunctionality("VK_EXT_descriptor_indexing"); const auto& indexingFeatures = context.getDescriptorIndexingFeatures(); if (!indexingFeatures.descriptorBindingPartiallyBound) TCU_THROW(NotSupportedError, "Partially bound bindings not supported"); } void requireVariableDescriptorCount (Context& context) { context.requireDeviceFunctionality("VK_EXT_descriptor_indexing"); const auto& indexingFeatures = context.getDescriptorIndexingFeatures(); if (!indexingFeatures.descriptorBindingVariableDescriptorCount) TCU_THROW(NotSupportedError, "Variable descriptor count not supported"); } // Calculates the set of used descriptor types for a given set and iteration count, for bindings matching a predicate. std::set getUsedDescriptorTypes (const DescriptorSet& descriptorSet, deUint32 numIterations, bool (*predicate)(const BindingInterface* binding)) { std::set usedDescriptorTypes; for (size_t bindingIdx = 0; bindingIdx < descriptorSet.numBindings(); ++bindingIdx) { const auto bindingPtr = descriptorSet.getBinding(bindingIdx); if (predicate(bindingPtr)) { for (deUint32 iter = 0u; iter < numIterations; ++iter) { const auto descTypes = bindingPtr->typesAtIteration(iter); usedDescriptorTypes.insert(begin(descTypes), end(descTypes)); } } } return usedDescriptorTypes; } std::set getAllUsedDescriptorTypes (const DescriptorSet& descriptorSet, deUint32 numIterations) { return getUsedDescriptorTypes(descriptorSet, numIterations, [] (const BindingInterface*) { return true; }); } std::set getUsedArrayDescriptorTypes (const DescriptorSet& descriptorSet, deUint32 numIterations) { return getUsedDescriptorTypes(descriptorSet, numIterations, [] (const BindingInterface* b) { return b->isArray(); }); } // Are we testing a vertex pipeline stage? bool isVertexStage (TestingStage stage) { switch (stage) { case TestingStage::VERTEX: case TestingStage::TESS_CONTROL: case TestingStage::TESS_EVAL: case TestingStage::GEOMETRY: return true; default: break; } return false; } void MutableTypesTest::checkSupport (Context& context) const { context.requireDeviceFunctionality("VK_VALVE_mutable_descriptor_type"); // Check ray tracing if needed. const bool rayTracing = isRayTracingStage(m_params.testingStage); if (rayTracing) { context.requireDeviceFunctionality("VK_KHR_acceleration_structure"); context.requireDeviceFunctionality("VK_KHR_ray_tracing_pipeline"); } // Check if ray queries are needed. Ray queries are used to verify acceleration structure descriptors. const bool rayQueriesNeeded = usesAccelerationStructures(*m_params.descriptorSet); if (rayQueriesNeeded) { context.requireDeviceFunctionality("VK_KHR_acceleration_structure"); context.requireDeviceFunctionality("VK_KHR_ray_query"); } // We'll use iterations to check each mutable type, as needed. const auto numIterations = m_params.descriptorSet->maxTypes(); if (m_params.descriptorSet->lastBindingIsUnbounded()) requireVariableDescriptorCount(context); for (deUint32 iter = 0u; iter < numIterations; ++iter) { if (m_params.descriptorSet->needsAliasing(iter)) { requirePartiallyBound(context); break; } } if (m_params.updateMoment == UpdateMoment::UPDATE_AFTER_BIND) { // Check update after bind for each used descriptor type. const auto& usedDescriptorTypes = getAllUsedDescriptorTypes(*m_params.descriptorSet, numIterations); const auto& indexingFeatures = context.getDescriptorIndexingFeatures(); for (const auto& descType : usedDescriptorTypes) { switch (descType) { case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: if (!indexingFeatures.descriptorBindingUniformBufferUpdateAfterBind) TCU_THROW(NotSupportedError, "Update-after-bind not supported for uniform buffers"); break; case VK_DESCRIPTOR_TYPE_SAMPLER: case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: if (!indexingFeatures.descriptorBindingSampledImageUpdateAfterBind) TCU_THROW(NotSupportedError, "Update-after-bind not supported for samplers and sampled images"); break; case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: if (!indexingFeatures.descriptorBindingStorageImageUpdateAfterBind) TCU_THROW(NotSupportedError, "Update-after-bind not supported for storage images"); break; case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: if (!indexingFeatures.descriptorBindingStorageBufferUpdateAfterBind) TCU_THROW(NotSupportedError, "Update-after-bind not supported for storage buffers"); break; case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: if (!indexingFeatures.descriptorBindingUniformTexelBufferUpdateAfterBind) TCU_THROW(NotSupportedError, "Update-after-bind not supported for uniform texel buffers"); break; case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: if (!indexingFeatures.descriptorBindingStorageTexelBufferUpdateAfterBind) TCU_THROW(NotSupportedError, "Update-after-bind not supported for storage texel buffers"); break; case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: TCU_THROW(InternalError, "Tests do not support update-after-bind with input attachments"); case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT: { // Just in case we ever mix some of these in. context.requireDeviceFunctionality("VK_EXT_inline_uniform_block"); const auto& iubFeatures = context.getInlineUniformBlockFeaturesEXT(); if (!iubFeatures.descriptorBindingInlineUniformBlockUpdateAfterBind) TCU_THROW(NotSupportedError, "Update-after-bind not supported for inline uniform blocks"); } break; case VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR: { // Just in case we ever mix some of these in. context.requireDeviceFunctionality("VK_KHR_acceleration_structure"); const auto& asFeatures = context.getAccelerationStructureFeatures(); if (!asFeatures.descriptorBindingAccelerationStructureUpdateAfterBind) TCU_THROW(NotSupportedError, "Update-after-bind not supported for acceleration structures"); } break; case VK_DESCRIPTOR_TYPE_MUTABLE_VALVE: TCU_THROW(InternalError, "Found VK_DESCRIPTOR_TYPE_MUTABLE_VALVE in list of used descriptor types"); default: TCU_THROW(InternalError, "Unexpected descriptor type found in list of used descriptor types: " + de::toString(descType)); } } } if (m_params.arrayAccessType == ArrayAccessType::PUSH_CONSTANT) { // These require dynamically uniform indices. const auto& usedDescriptorTypes = getUsedArrayDescriptorTypes(*m_params.descriptorSet, numIterations); const auto& features = context.getDeviceFeatures(); const auto descriptorIndexingSupported = context.isDeviceFunctionalitySupported("VK_EXT_descriptor_indexing"); const auto& indexingFeatures = context.getDescriptorIndexingFeatures(); for (const auto& descType : usedDescriptorTypes) { switch (descType) { case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: if (!features.shaderUniformBufferArrayDynamicIndexing) TCU_THROW(NotSupportedError, "Dynamic indexing not supported for uniform buffers"); break; case VK_DESCRIPTOR_TYPE_SAMPLER: case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: if (!features.shaderSampledImageArrayDynamicIndexing) TCU_THROW(NotSupportedError, "Dynamic indexing not supported for samplers and sampled images"); break; case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: if (!features.shaderStorageImageArrayDynamicIndexing) TCU_THROW(NotSupportedError, "Dynamic indexing not supported for storage images"); break; case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: if (!features.shaderStorageBufferArrayDynamicIndexing) TCU_THROW(NotSupportedError, "Dynamic indexing not supported for storage buffers"); break; case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: if (!descriptorIndexingSupported || !indexingFeatures.shaderUniformTexelBufferArrayDynamicIndexing) TCU_THROW(NotSupportedError, "Dynamic indexing not supported for uniform texel buffers"); break; case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: if (!descriptorIndexingSupported || !indexingFeatures.shaderStorageTexelBufferArrayDynamicIndexing) TCU_THROW(NotSupportedError, "Dynamic indexing not supported for storage texel buffers"); break; case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: if (!descriptorIndexingSupported || !indexingFeatures.shaderInputAttachmentArrayDynamicIndexing) TCU_THROW(NotSupportedError, "Dynamic indexing not supported for input attachments"); break; case VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR: context.requireDeviceFunctionality("VK_KHR_acceleration_structure"); break; case VK_DESCRIPTOR_TYPE_MUTABLE_VALVE: TCU_THROW(InternalError, "Found VK_DESCRIPTOR_TYPE_MUTABLE_VALVE in list of used array descriptor types"); default: TCU_THROW(InternalError, "Unexpected descriptor type found in list of used descriptor types: " + de::toString(descType)); } } } // Check layout support. { const auto& vkd = context.getDeviceInterface(); const auto device = context.getDevice(); const auto stageFlags = m_params.getStageFlags(); { const auto layoutCreateFlags = m_params.getDstLayoutCreateFlags(); const auto supported = m_params.descriptorSet->checkDescriptorSetLayout(vkd, device, stageFlags, layoutCreateFlags); if (!supported) TCU_THROW(NotSupportedError, "Required descriptor set layout not supported"); } if (m_params.updateType == UpdateType::COPY) { const auto layoutCreateFlags = m_params.getSrcLayoutCreateFlags(); const auto supported = m_params.descriptorSet->checkDescriptorSetLayout(vkd, device, stageFlags, layoutCreateFlags); if (!supported) TCU_THROW(NotSupportedError, "Required descriptor set layout for source set not supported"); // Check specific layouts for the different source sets are supported. for (deUint32 iter = 0u; iter < numIterations; ++iter) { const auto srcSet = m_params.descriptorSet->genSourceSet(m_params.sourceSetStrategy, iter); const auto srcLayoutSupported = srcSet->checkDescriptorSetLayout(vkd, device, stageFlags, layoutCreateFlags); if (!srcLayoutSupported) TCU_THROW(NotSupportedError, "Descriptor set layout for source set at iteration " + de::toString(iter) + " not supported"); } } } // Check supported stores and stages. const bool vertexStage = isVertexStage(m_params.testingStage); const bool fragmentStage = (m_params.testingStage == TestingStage::FRAGMENT); const bool geometryStage = (m_params.testingStage == TestingStage::GEOMETRY); const bool tessellation = (m_params.testingStage == TestingStage::TESS_CONTROL || m_params.testingStage == TestingStage::TESS_EVAL); const auto& features = context.getDeviceFeatures(); if (vertexStage && !features.vertexPipelineStoresAndAtomics) TCU_THROW(NotSupportedError, "Vertex pipeline stores and atomics not supported"); if (fragmentStage && !features.fragmentStoresAndAtomics) TCU_THROW(NotSupportedError, "Fragment shader stores and atomics not supported"); if (geometryStage && !features.geometryShader) TCU_THROW(NotSupportedError, "Geometry shader not supported"); if (tessellation && !features.tessellationShader) TCU_THROW(NotSupportedError, "Tessellation shaders not supported"); } // What to do at each iteration step. Used to apply UPDATE_AFTER_BIND or not. enum class Step { UPDATE = 0, BIND, }; // Create render pass. Move buildRenderPass (const DeviceInterface& vkd, VkDevice device, const std::vector& resources) { const auto imageFormat = getDescriptorImageFormat(); std::vector attachmentDescriptions; std::vector attachmentReferences; std::vector attachmentIndices; for (const auto& resource : resources) { if (resource.descriptorType == VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT) { const auto nextIndex = static_cast(attachmentDescriptions.size()); const VkAttachmentDescription description = { 0u, // VkAttachmentDescriptionFlags flags; imageFormat, // VkFormat format; VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples; VK_ATTACHMENT_LOAD_OP_LOAD, // VkAttachmentLoadOp loadOp; VK_ATTACHMENT_STORE_OP_DONT_CARE, // VkAttachmentStoreOp storeOp; VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp stencilLoadOp; VK_ATTACHMENT_STORE_OP_DONT_CARE, // VkAttachmentStoreOp stencilStoreOp; VK_IMAGE_LAYOUT_GENERAL, // VkImageLayout initialLayout; VK_IMAGE_LAYOUT_GENERAL, // VkImageLayout finalLayout; }; const VkAttachmentReference reference = { nextIndex, VK_IMAGE_LAYOUT_GENERAL }; attachmentIndices.push_back(nextIndex); attachmentDescriptions.push_back(description); attachmentReferences.push_back(reference); } } const auto attachmentCount = static_cast(attachmentDescriptions.size()); DE_ASSERT(attachmentCount == static_cast(attachmentIndices.size())); DE_ASSERT(attachmentCount == static_cast(attachmentReferences.size())); const VkSubpassDescription subpassDescription = { 0u, // VkSubpassDescriptionFlags flags; VK_PIPELINE_BIND_POINT_GRAPHICS, // VkPipelineBindPoint pipelineBindPoint; attachmentCount, // deUint32 inputAttachmentCount; de::dataOrNull(attachmentReferences), // const VkAttachmentReference* pInputAttachments; 0u, // deUint32 colorAttachmentCount; nullptr, // const VkAttachmentReference* pColorAttachments; 0u, // const VkAttachmentReference* pResolveAttachments; nullptr, // const VkAttachmentReference* pDepthStencilAttachment; 0u, // deUint32 preserveAttachmentCount; nullptr, // const deUint32* pPreserveAttachments; }; const VkRenderPassCreateInfo renderPassCreateInfo = { VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, // VkStructureType sType; nullptr, // const void* pNext; 0u, // VkRenderPassCreateFlags flags; static_cast(attachmentDescriptions.size()), // deUint32 attachmentCount; de::dataOrNull(attachmentDescriptions), // const VkAttachmentDescription* pAttachments; 1u, // deUint32 subpassCount; &subpassDescription, // const VkSubpassDescription* pSubpasses; 0u, // deUint32 dependencyCount; nullptr, // const VkSubpassDependency* pDependencies; }; return createRenderPass(vkd, device, &renderPassCreateInfo); } // Create a graphics pipeline. Move buildGraphicsPipeline (const DeviceInterface& vkd, VkDevice device, VkPipelineLayout pipelineLayout, VkShaderModule vertModule, VkShaderModule tescModule, VkShaderModule teseModule, VkShaderModule geomModule, VkShaderModule fragModule, VkRenderPass renderPass) { const auto extent = getDefaultExtent(); const std::vector viewports (1u, makeViewport(extent)); const std::vector scissors (1u, makeRect2D(extent)); const auto hasTess = (tescModule != DE_NULL || teseModule != DE_NULL); const auto topology = (hasTess ? VK_PRIMITIVE_TOPOLOGY_PATCH_LIST : VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST); const VkPipelineVertexInputStateCreateInfo vertexInputStateCreateInfo = initVulkanStructure(); const VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCreateInfo = { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, // VkStructureType sType; nullptr, // const void* pNext; 0u, // VkPipelineInputAssemblyStateCreateFlags flags; topology, // VkPrimitiveTopology topology; VK_FALSE, // VkBool32 primitiveRestartEnable; }; const VkPipelineTessellationStateCreateInfo tessellationStateCreateInfo = { VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO, // VkStructureType sType; nullptr, // const void* pNext; 0u, // VkPipelineTessellationStateCreateFlags flags; (hasTess ? 3u : 0u), // deUint32 patchControlPoints; }; const VkPipelineViewportStateCreateInfo viewportStateCreateInfo = { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, // VkStructureType sType; nullptr, // const void* pNext; 0u, // VkPipelineViewportStateCreateFlags flags; static_cast(viewports.size()), // deUint32 viewportCount; de::dataOrNull(viewports), // const VkViewport* pViewports; static_cast(scissors.size()), // deUint32 scissorCount; de::dataOrNull(scissors), // const VkRect2D* pScissors; }; const VkPipelineRasterizationStateCreateInfo rasterizationStateCreateInfo = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, // VkStructureType sType; nullptr, // const void* pNext; 0u, // VkPipelineRasterizationStateCreateFlags flags; VK_FALSE, // VkBool32 depthClampEnable; (fragModule == DE_NULL ? VK_TRUE : VK_FALSE), // VkBool32 rasterizerDiscardEnable; VK_POLYGON_MODE_FILL, // VkPolygonMode polygonMode; VK_CULL_MODE_NONE, // VkCullModeFlags cullMode; VK_FRONT_FACE_CLOCKWISE, // VkFrontFace frontFace; VK_FALSE, // VkBool32 depthBiasEnable; 0.0f, // float depthBiasConstantFactor; 0.0f, // float depthBiasClamp; 0.0f, // float depthBiasSlopeFactor; 1.0f, // float lineWidth; }; const VkPipelineMultisampleStateCreateInfo multisampleStateCreateInfo = { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, // VkStructureType sType; nullptr, // const void* pNext; 0u, // VkPipelineMultisampleStateCreateFlags flags; VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits rasterizationSamples; VK_FALSE, // VkBool32 sampleShadingEnable; 1.0f, // float minSampleShading; nullptr, // const VkSampleMask* pSampleMask; VK_FALSE, // VkBool32 alphaToCoverageEnable; VK_FALSE, // VkBool32 alphaToOneEnable; }; const VkPipelineDepthStencilStateCreateInfo depthStencilStateCreateInfo = initVulkanStructure(); const VkPipelineColorBlendStateCreateInfo colorBlendStateCreateInfo = initVulkanStructure(); return makeGraphicsPipeline(vkd, device, pipelineLayout, vertModule, tescModule, teseModule, geomModule, fragModule, renderPass, 0u, &vertexInputStateCreateInfo, &inputAssemblyStateCreateInfo, (hasTess ? &tessellationStateCreateInfo : nullptr), &viewportStateCreateInfo, &rasterizationStateCreateInfo, &multisampleStateCreateInfo, &depthStencilStateCreateInfo, &colorBlendStateCreateInfo, nullptr); } Move buildFramebuffer (const DeviceInterface& vkd, VkDevice device, VkRenderPass renderPass, const std::vector& resources) { const auto extent = getDefaultExtent(); std::vector inputAttachments; for (const auto& resource : resources) { if (resource.descriptorType == VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT) inputAttachments.push_back(resource.imageView.get()); } const VkFramebufferCreateInfo framebufferCreateInfo = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, // VkStructureType sType; nullptr, // const void* pNext; 0u, // VkFramebufferCreateFlags flags; renderPass, // VkRenderPass renderPass; static_cast(inputAttachments.size()), // deUint32 attachmentCount; de:: dataOrNull(inputAttachments), // const VkImageView* pAttachments; extent.width, // deUint32 width; extent.height, // deUint32 height; extent.depth, // deUint32 layers; }; return createFramebuffer(vkd, device, &framebufferCreateInfo); } tcu::TestStatus MutableTypesInstance::iterate () { const auto device = m_context.getDevice(); const auto physDev = m_context.getPhysicalDevice(); const auto qIndex = m_context.getUniversalQueueFamilyIndex(); const auto queue = m_context.getUniversalQueue(); const auto& vki = m_context.getInstanceInterface(); const auto& vkd = m_context.getDeviceInterface(); auto & alloc = m_context.getDefaultAllocator(); const auto& paramSet = m_params.descriptorSet; const auto numIterations = paramSet->maxTypes(); const bool useExternalImage = needsExternalImage(*m_params.descriptorSet); const bool useExternalSampler = needsExternalSampler(*m_params.descriptorSet); const auto stageFlags = m_params.getStageFlags(); const bool srcSetNeeded = (m_params.updateType == UpdateType::COPY); const bool updateAfterBind = (m_params.updateMoment == UpdateMoment::UPDATE_AFTER_BIND); const auto bindPoint = m_params.getBindPoint(); const bool rayTracing = isRayTracingStage(m_params.testingStage); const bool useAABBs = (m_params.testingStage == TestingStage::INTERSECTION); // Resources for each iteration. std::vector> allResources; allResources.reserve(numIterations); // Command pool. const auto cmdPool = makeCommandPool(vkd, device, qIndex); // Descriptor pool and set for the active (dst) descriptor set. const auto dstPoolFlags = m_params.getDstPoolCreateFlags(); const auto dstLayoutFlags = m_params.getDstLayoutCreateFlags(); const auto dstPool = paramSet->makeDescriptorPool(vkd, device, m_params.poolMutableStrategy, dstPoolFlags); const auto dstLayout = paramSet->makeDescriptorSetLayout(vkd, device, stageFlags, dstLayoutFlags); const auto varCount = paramSet->getVariableDescriptorCount(); using VariableCountInfoPtr = de::MovePtr; VariableCountInfoPtr dstVariableCountInfo; if (varCount) { dstVariableCountInfo = VariableCountInfoPtr(new VkDescriptorSetVariableDescriptorCountAllocateInfo); *dstVariableCountInfo = initVulkanStructure(); dstVariableCountInfo->descriptorSetCount = 1u; dstVariableCountInfo->pDescriptorCounts = &(varCount.get()); } const auto dstSet = makeDescriptorSet(vkd, device, dstPool.get(), dstLayout.get(), dstVariableCountInfo.get()); // Source pool and set (optional). const auto srcPoolFlags = m_params.getSrcPoolCreateFlags(); const auto srcLayoutFlags = m_params.getSrcLayoutCreateFlags(); DescriptorSetPtr iterationSrcSet; Move srcPool; Move srcLayout; Move srcSet; // Extra set for external resources and output buffer. std::vector extraResources; extraResources.emplace_back(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, vkd, device, alloc, qIndex, queue, useAABBs, 0u, numIterations); if (useExternalImage) extraResources.emplace_back(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, vkd, device, alloc, qIndex, queue, useAABBs, getExternalSampledImageValue()); if (useExternalSampler) extraResources.emplace_back(VK_DESCRIPTOR_TYPE_SAMPLER, vkd, device, alloc, qIndex, queue, useAABBs, 0u); if (rayTracing) extraResources.emplace_back(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, vkd, device, alloc, qIndex, queue, useAABBs, 0u); Move extraPool; { DescriptorPoolBuilder poolBuilder; poolBuilder.addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); if (useExternalImage) poolBuilder.addType(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE); if (useExternalSampler) poolBuilder.addType(VK_DESCRIPTOR_TYPE_SAMPLER); if (rayTracing) poolBuilder.addType(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR); extraPool = poolBuilder.build(vkd, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u); } Move extraLayout; { DescriptorSetLayoutBuilder layoutBuilder; layoutBuilder.addBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1u, stageFlags, nullptr); if (useExternalImage) layoutBuilder.addBinding(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1u, stageFlags, nullptr); if (useExternalSampler) layoutBuilder.addBinding(VK_DESCRIPTOR_TYPE_SAMPLER, 1u, stageFlags, nullptr); if (rayTracing) { // The extra acceleration structure is used from the ray generation shader only. layoutBuilder.addBinding(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 1u, VK_SHADER_STAGE_RAYGEN_BIT_KHR, nullptr); } extraLayout = layoutBuilder.build(vkd, device); } const auto extraSet = makeDescriptorSet(vkd, device, extraPool.get(), extraLayout.get()); // Update extra set. using DescriptorBufferInfoPtr = de::MovePtr; using DescriptorImageInfoPtr = de::MovePtr; using DescriptorASInfoPtr = de::MovePtr; deUint32 bindingCount = 0u; DescriptorBufferInfoPtr bufferInfoPtr; DescriptorImageInfoPtr imageInfoPtr; DescriptorImageInfoPtr samplerInfoPtr; DescriptorASInfoPtr asWriteInfoPtr; const auto outputBufferSize = static_cast(sizeof(deUint32) * static_cast(numIterations)); bufferInfoPtr = DescriptorBufferInfoPtr(new VkDescriptorBufferInfo(makeDescriptorBufferInfo(extraResources[bindingCount++].bufferWithMemory->get(), 0ull, outputBufferSize))); if (useExternalImage) imageInfoPtr = DescriptorImageInfoPtr(new VkDescriptorImageInfo(makeDescriptorImageInfo(DE_NULL, extraResources[bindingCount++].imageView.get(), VK_IMAGE_LAYOUT_GENERAL))); if (useExternalSampler) samplerInfoPtr = DescriptorImageInfoPtr(new VkDescriptorImageInfo(makeDescriptorImageInfo(extraResources[bindingCount++].sampler.get(), DE_NULL, VK_IMAGE_LAYOUT_GENERAL))); if (rayTracing) { asWriteInfoPtr = DescriptorASInfoPtr(new VkWriteDescriptorSetAccelerationStructureKHR); *asWriteInfoPtr = initVulkanStructure(); asWriteInfoPtr->accelerationStructureCount = 1u; asWriteInfoPtr->pAccelerationStructures = extraResources[bindingCount++].asData.tlas.get()->getPtr(); } { bindingCount = 0u; DescriptorSetUpdateBuilder updateBuilder; updateBuilder.writeSingle(extraSet.get(), DescriptorSetUpdateBuilder::Location::binding(bindingCount++), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, bufferInfoPtr.get()); if (useExternalImage) updateBuilder.writeSingle(extraSet.get(), DescriptorSetUpdateBuilder::Location::binding(bindingCount++), VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, imageInfoPtr.get()); if (useExternalSampler) updateBuilder.writeSingle(extraSet.get(), DescriptorSetUpdateBuilder::Location::binding(bindingCount++), VK_DESCRIPTOR_TYPE_SAMPLER, samplerInfoPtr.get()); if (rayTracing) updateBuilder.writeSingle(extraSet.get(), DescriptorSetUpdateBuilder::Location::binding(bindingCount++), VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, asWriteInfoPtr.get()); updateBuilder.update(vkd, device); } // Push constants. const deUint32 zero = 0u; const VkPushConstantRange pcRange = {stageFlags, 0u /*offset*/, static_cast(sizeof(zero)) /*size*/ }; // Needed for some test variants. Move vertPassthrough; Move tesePassthrough; Move tescPassthrough; Move rgenPassthrough; Move missPassthrough; if (m_params.testingStage == TestingStage::FRAGMENT || m_params.testingStage == TestingStage::GEOMETRY || m_params.testingStage == TestingStage::TESS_CONTROL || m_params.testingStage == TestingStage::TESS_EVAL) { vertPassthrough = createShaderModule(vkd, device, m_context.getBinaryCollection().get("vert"), 0u); } if (m_params.testingStage == TestingStage::TESS_CONTROL) { tesePassthrough = createShaderModule(vkd, device, m_context.getBinaryCollection().get("tese"), 0u); } if (m_params.testingStage == TestingStage::TESS_EVAL) { tescPassthrough = createShaderModule(vkd, device, m_context.getBinaryCollection().get("tesc"), 0u); } if (m_params.testingStage == TestingStage::CLOSEST_HIT || m_params.testingStage == TestingStage::ANY_HIT || m_params.testingStage == TestingStage::INTERSECTION || m_params.testingStage == TestingStage::MISS || m_params.testingStage == TestingStage::CALLABLE) { rgenPassthrough = createShaderModule(vkd, device, m_context.getBinaryCollection().get("rgen"), 0u); } if (m_params.testingStage == TestingStage::INTERSECTION) { missPassthrough = createShaderModule(vkd, device, m_context.getBinaryCollection().get("miss"), 0u); } for (deUint32 iteration = 0u; iteration < numIterations; ++iteration) { // Generate source set for the current iteration. if (srcSetNeeded) { // Free previous descriptor set before rebuilding the pool. srcSet = Move(); iterationSrcSet = paramSet->genSourceSet(m_params.sourceSetStrategy, iteration); srcPool = iterationSrcSet->makeDescriptorPool(vkd, device, m_params.poolMutableStrategy, srcPoolFlags); srcLayout = iterationSrcSet->makeDescriptorSetLayout(vkd, device, stageFlags, srcLayoutFlags); const auto srcVarCount = iterationSrcSet->getVariableDescriptorCount(); VariableCountInfoPtr srcVariableCountInfo; if (srcVarCount) { srcVariableCountInfo = VariableCountInfoPtr(new VkDescriptorSetVariableDescriptorCountAllocateInfo); *srcVariableCountInfo = initVulkanStructure(); srcVariableCountInfo->descriptorSetCount = 1u; srcVariableCountInfo->pDescriptorCounts = &(srcVarCount.get()); } srcSet = makeDescriptorSet(vkd, device, srcPool.get(), srcLayout.get(), srcVariableCountInfo.get()); } // Set layouts and sets used in the pipeline. const std::vector setLayouts = {dstLayout.get(), extraLayout.get()}; const std::vector usedSets = {dstSet.get(), extraSet.get()}; // Create resources. allResources.emplace_back(paramSet->createResources(vkd, device, alloc, qIndex, queue, iteration, useAABBs)); const auto& resources = allResources.back(); // Make pipeline for the current iteration. const auto pipelineLayout = makePipelineLayout(vkd, device, static_cast(setLayouts.size()), de::dataOrNull(setLayouts), 1u, &pcRange); const auto moduleName = shaderName(iteration); const auto shaderModule = createShaderModule(vkd, device, m_context.getBinaryCollection().get(moduleName), 0u); Move pipeline; Move renderPass; Move framebuffer; deUint32 shaderGroupHandleSize = 0u; deUint32 shaderGroupBaseAlignment = 1u; de::MovePtr raygenSBT; de::MovePtr missSBT; de::MovePtr hitSBT; de::MovePtr callableSBT; VkStridedDeviceAddressRegionKHR raygenSBTRegion = makeStridedDeviceAddressRegionKHR(DE_NULL, 0, 0); VkStridedDeviceAddressRegionKHR missSBTRegion = makeStridedDeviceAddressRegionKHR(DE_NULL, 0, 0); VkStridedDeviceAddressRegionKHR hitSBTRegion = makeStridedDeviceAddressRegionKHR(DE_NULL, 0, 0); VkStridedDeviceAddressRegionKHR callableSBTRegion = makeStridedDeviceAddressRegionKHR(DE_NULL, 0, 0); if (bindPoint == VK_PIPELINE_BIND_POINT_COMPUTE) pipeline = makeComputePipeline(vkd, device, pipelineLayout.get(), 0u, shaderModule.get(), 0u, nullptr); else if (bindPoint == VK_PIPELINE_BIND_POINT_GRAPHICS) { VkShaderModule vertModule = DE_NULL; VkShaderModule teseModule = DE_NULL; VkShaderModule tescModule = DE_NULL; VkShaderModule geomModule = DE_NULL; VkShaderModule fragModule = DE_NULL; if (m_params.testingStage == TestingStage::VERTEX) vertModule = shaderModule.get(); else if (m_params.testingStage == TestingStage::FRAGMENT) { vertModule = vertPassthrough.get(); fragModule = shaderModule.get(); } else if (m_params.testingStage == TestingStage::GEOMETRY) { vertModule = vertPassthrough.get(); geomModule = shaderModule.get(); } else if (m_params.testingStage == TestingStage::TESS_CONTROL) { vertModule = vertPassthrough.get(); teseModule = tesePassthrough.get(); tescModule = shaderModule.get(); } else if (m_params.testingStage == TestingStage::TESS_EVAL) { vertModule = vertPassthrough.get(); tescModule = tescPassthrough.get(); teseModule = shaderModule.get(); } else DE_ASSERT(false); renderPass = buildRenderPass(vkd, device, resources); pipeline = buildGraphicsPipeline(vkd, device, pipelineLayout.get(), vertModule, tescModule, teseModule, geomModule, fragModule, renderPass.get()); framebuffer = buildFramebuffer(vkd, device, renderPass.get(), resources); } else if (bindPoint == VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR) { const auto rayTracingPipeline = de::newMovePtr(); const auto rayTracingPropertiesKHR = makeRayTracingProperties(vki, physDev); shaderGroupHandleSize = rayTracingPropertiesKHR->getShaderGroupHandleSize(); shaderGroupBaseAlignment = rayTracingPropertiesKHR->getShaderGroupBaseAlignment(); VkShaderModule rgenModule = DE_NULL; VkShaderModule isecModule = DE_NULL; VkShaderModule ahitModule = DE_NULL; VkShaderModule chitModule = DE_NULL; VkShaderModule missModule = DE_NULL; VkShaderModule callModule = DE_NULL; const deUint32 rgenGroup = 0u; deUint32 hitGroup = 0u; deUint32 missGroup = 0u; deUint32 callGroup = 0u; if (m_params.testingStage == TestingStage::RAY_GEN) { rgenModule = shaderModule.get(); rayTracingPipeline->addShader(VK_SHADER_STAGE_RAYGEN_BIT_KHR, rgenModule, rgenGroup); } else if (m_params.testingStage == TestingStage::INTERSECTION) { hitGroup = 1u; missGroup = 2u; rgenModule = rgenPassthrough.get(); missModule = missPassthrough.get(); isecModule = shaderModule.get(); rayTracingPipeline->addShader(VK_SHADER_STAGE_RAYGEN_BIT_KHR, rgenModule, rgenGroup); rayTracingPipeline->addShader(VK_SHADER_STAGE_INTERSECTION_BIT_KHR, isecModule, hitGroup); rayTracingPipeline->addShader(VK_SHADER_STAGE_MISS_BIT_KHR, missModule, missGroup); } else if (m_params.testingStage == TestingStage::ANY_HIT) { hitGroup = 1u; rgenModule = rgenPassthrough.get(); ahitModule = shaderModule.get(); rayTracingPipeline->addShader(VK_SHADER_STAGE_RAYGEN_BIT_KHR, rgenModule, rgenGroup); rayTracingPipeline->addShader(VK_SHADER_STAGE_ANY_HIT_BIT_KHR, ahitModule, hitGroup); } else if (m_params.testingStage == TestingStage::CLOSEST_HIT) { hitGroup = 1u; rgenModule = rgenPassthrough.get(); chitModule = shaderModule.get(); rayTracingPipeline->addShader(VK_SHADER_STAGE_RAYGEN_BIT_KHR, rgenModule, rgenGroup); rayTracingPipeline->addShader(VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR, chitModule, hitGroup); } else if (m_params.testingStage == TestingStage::MISS) { missGroup = 1u; rgenModule = rgenPassthrough.get(); missModule = shaderModule.get(); rayTracingPipeline->addShader(VK_SHADER_STAGE_RAYGEN_BIT_KHR, rgenModule, rgenGroup); rayTracingPipeline->addShader(VK_SHADER_STAGE_MISS_BIT_KHR, missModule, missGroup); } else if (m_params.testingStage == TestingStage::CALLABLE) { callGroup = 1u; rgenModule = rgenPassthrough.get(); callModule = shaderModule.get(); rayTracingPipeline->addShader(VK_SHADER_STAGE_RAYGEN_BIT_KHR, rgenModule, rgenGroup); rayTracingPipeline->addShader(VK_SHADER_STAGE_CALLABLE_BIT_KHR, callModule, callGroup); } else DE_ASSERT(false); pipeline = rayTracingPipeline->createPipeline(vkd, device, pipelineLayout.get()); raygenSBT = rayTracingPipeline->createShaderBindingTable(vkd, device, pipeline.get(), alloc, shaderGroupHandleSize, shaderGroupBaseAlignment, rgenGroup, 1u); raygenSBTRegion = makeStridedDeviceAddressRegionKHR(getBufferDeviceAddress(vkd, device, raygenSBT->get(), 0ull), shaderGroupHandleSize, shaderGroupHandleSize); if (missGroup > 0u) { missSBT = rayTracingPipeline->createShaderBindingTable(vkd, device, pipeline.get(), alloc, shaderGroupHandleSize, shaderGroupBaseAlignment, missGroup, 1u); missSBTRegion = makeStridedDeviceAddressRegionKHR(getBufferDeviceAddress(vkd, device, missSBT->get(), 0ull), shaderGroupHandleSize, shaderGroupHandleSize); } if (hitGroup > 0u) { hitSBT = rayTracingPipeline->createShaderBindingTable(vkd, device, pipeline.get(), alloc, shaderGroupHandleSize, shaderGroupBaseAlignment, hitGroup, 1u); hitSBTRegion = makeStridedDeviceAddressRegionKHR(getBufferDeviceAddress(vkd, device, hitSBT->get(), 0ull), shaderGroupHandleSize, shaderGroupHandleSize); } if (callGroup > 0u) { callableSBT = rayTracingPipeline->createShaderBindingTable(vkd, device, pipeline.get(), alloc, shaderGroupHandleSize, shaderGroupBaseAlignment, callGroup, 1u); callableSBTRegion = makeStridedDeviceAddressRegionKHR(getBufferDeviceAddress(vkd, device, callableSBT->get(), 0ull), shaderGroupHandleSize, shaderGroupHandleSize); } } else DE_ASSERT(false); // Command buffer for the current iteration. const auto cmdBufferPtr = allocateCommandBuffer(vkd, device, cmdPool.get(), VK_COMMAND_BUFFER_LEVEL_PRIMARY); const auto cmdBuffer = cmdBufferPtr.get(); beginCommandBuffer(vkd, cmdBuffer); const Step steps[] = { (updateAfterBind ? Step::BIND : Step::UPDATE), (updateAfterBind ? Step::UPDATE : Step::BIND) }; for (const auto& step : steps) { if (step == Step::BIND) { vkd.cmdBindPipeline(cmdBuffer, bindPoint, pipeline.get()); vkd.cmdBindDescriptorSets(cmdBuffer, bindPoint, pipelineLayout.get(), 0u, static_cast(usedSets.size()), de::dataOrNull(usedSets), 0u, nullptr); } else // Step::UPDATE { if (srcSetNeeded) { // Note: these operations need to be called on paramSet and not iterationSrcSet. The latter is a compatible set // that's correct and contains compatible bindings but, when a binding has been changed from non-mutable to // mutable or to an extended mutable type, the list of descriptor types for the mutable bindings in // iterationSrcSet are not in iteration order like they are in the original set and must not be taken into // account to update or copy sets. paramSet->updateDescriptorSet(vkd, device, srcSet.get(), iteration, resources); paramSet->copyDescriptorSet(vkd, device, srcSet.get(), dstSet.get()); } else { paramSet->updateDescriptorSet(vkd, device, dstSet.get(), iteration, resources); } } } // Run shader. vkd.cmdPushConstants(cmdBuffer, pipelineLayout.get(), stageFlags, 0u, static_cast(sizeof(zero)), &zero); if (bindPoint == VK_PIPELINE_BIND_POINT_COMPUTE) vkd.cmdDispatch(cmdBuffer, 1u, 1u, 1u); else if (bindPoint == VK_PIPELINE_BIND_POINT_GRAPHICS) { const auto extent = getDefaultExtent(); const auto renderArea = makeRect2D(extent); beginRenderPass(vkd, cmdBuffer, renderPass.get(), framebuffer.get(), renderArea); vkd.cmdDraw(cmdBuffer, 3u, 1u, 0u, 0u); endRenderPass(vkd, cmdBuffer); } else if (bindPoint == VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR) { vkd.cmdTraceRaysKHR(cmdBuffer, &raygenSBTRegion, &missSBTRegion, &hitSBTRegion, &callableSBTRegion, 1u, 1u, 1u); } else DE_ASSERT(false); endCommandBuffer(vkd, cmdBuffer); submitCommandsAndWait(vkd, device, queue, cmdBuffer); // Verify output buffer. { const auto outputBufferVal = extraResources[0].getStoredValue(vkd, device, alloc, qIndex, queue, iteration); DE_ASSERT(static_cast(outputBufferVal)); const auto expectedValue = getExpectedOutputBufferValue(); if (outputBufferVal.get() != expectedValue) { std::ostringstream msg; msg << "Iteration " << iteration << ": unexpected value found in output buffer (expected " << expectedValue << " and found " << outputBufferVal.get() << ")"; TCU_FAIL(msg.str()); } } // Verify descriptor writes. { size_t resourcesOffset = 0; const auto writeMask = getStoredValueMask(); const auto numBindings = paramSet->numBindings(); for (deUint32 bindingIdx = 0u; bindingIdx < numBindings; ++bindingIdx) { const auto binding = paramSet->getBinding(bindingIdx); const auto bindingTypes = binding->typesAtIteration(iteration); for (size_t descriptorIdx = 0; descriptorIdx < bindingTypes.size(); ++descriptorIdx) { const auto& descriptorType = bindingTypes[descriptorIdx]; if (!isShaderWritable(descriptorType)) continue; const auto& resource = resources[resourcesOffset + descriptorIdx]; const auto initialValue = resource.initialValue; const auto storedValuePtr = resource.getStoredValue(vkd, device, alloc, qIndex, queue); DE_ASSERT(static_cast(storedValuePtr)); const auto storedValue = storedValuePtr.get(); const auto expectedValue = (initialValue | writeMask); if (expectedValue != storedValue) { std::ostringstream msg; msg << "Iteration " << iteration << ": descriptor at binding " << bindingIdx << " index " << descriptorIdx << " with type " << de::toString(descriptorType) << " contains unexpected value " << std::hex << storedValue << " (expected " << expectedValue << ")"; TCU_FAIL(msg.str()); } } resourcesOffset += bindingTypes.size(); } } } return tcu::TestStatus::pass("Pass"); } using GroupPtr = de::MovePtr; void createMutableTestVariants (tcu::TestContext& testCtx, tcu::TestCaseGroup* parentGroup, const DescriptorSetPtr& descriptorSet, const std::vector& stagesToTest) { const struct { UpdateType updateType; const char* name; } updateTypes[] = { {UpdateType::WRITE, "update_write"}, {UpdateType::COPY, "update_copy"}, }; const struct { SourceSetStrategy sourceSetStrategy; const char* name; } sourceStrategies[] = { {SourceSetStrategy::MUTABLE, "mutable_source"}, {SourceSetStrategy::NONMUTABLE, "nonmutable_source"}, {SourceSetStrategy::NO_SOURCE, "no_source"}, }; const struct { SourceSetType sourceSetType; const char* name; } sourceTypes[] = { {SourceSetType::NORMAL, "normal_source"}, {SourceSetType::HOST_ONLY, "host_only_source"}, {SourceSetType::NO_SOURCE, "no_source"}, }; const struct { PoolMutableStrategy poolMutableStrategy; const char* name; } poolStrategies[] = { {PoolMutableStrategy::KEEP_TYPES, "pool_same_types"}, {PoolMutableStrategy::NO_TYPES, "pool_no_types"}, {PoolMutableStrategy::EXPAND_TYPES, "pool_expand_types"}, }; const struct { UpdateMoment updateMoment; const char* name; } updateMoments[] = { {UpdateMoment::NORMAL, "pre_update"}, {UpdateMoment::UPDATE_AFTER_BIND, "update_after_bind"}, }; const struct { ArrayAccessType arrayAccessType; const char* name; } arrayAccessTypes[] = { {ArrayAccessType::CONSTANT, "index_constant"}, {ArrayAccessType::PUSH_CONSTANT, "index_push_constant"}, {ArrayAccessType::NO_ARRAY, "no_array"}, }; const struct StageAndName { TestingStage testingStage; const char* name; } testStageList[] = { {TestingStage::COMPUTE, "comp"}, {TestingStage::VERTEX, "vert"}, {TestingStage::TESS_CONTROL, "tesc"}, {TestingStage::TESS_EVAL, "tese"}, {TestingStage::GEOMETRY, "geom"}, {TestingStage::FRAGMENT, "frag"}, {TestingStage::RAY_GEN, "rgen"}, {TestingStage::INTERSECTION, "isec"}, {TestingStage::ANY_HIT, "ahit"}, {TestingStage::CLOSEST_HIT, "chit"}, {TestingStage::MISS, "miss"}, {TestingStage::CALLABLE, "call"}, }; const bool hasArrays = descriptorSet->hasArrays(); const bool hasInputAttachments = usesInputAttachments(*descriptorSet); for (const auto& ut : updateTypes) { GroupPtr updateGroup(new tcu::TestCaseGroup(testCtx, ut.name, "")); for (const auto& srcStrategy : sourceStrategies) { // Skip combinations that make no sense. if (ut.updateType == UpdateType::WRITE && srcStrategy.sourceSetStrategy != SourceSetStrategy::NO_SOURCE) continue; if (ut.updateType == UpdateType::COPY && srcStrategy.sourceSetStrategy == SourceSetStrategy::NO_SOURCE) continue; if (srcStrategy.sourceSetStrategy == SourceSetStrategy::NONMUTABLE && descriptorSet->needsAnyAliasing()) continue; GroupPtr srcStrategyGroup(new tcu::TestCaseGroup(testCtx, srcStrategy.name, "")); for (const auto& srcType : sourceTypes) { // Skip combinations that make no sense. if (ut.updateType == UpdateType::WRITE && srcType.sourceSetType != SourceSetType::NO_SOURCE) continue; if (ut.updateType == UpdateType::COPY && srcType.sourceSetType == SourceSetType::NO_SOURCE) continue; GroupPtr srcTypeGroup(new tcu::TestCaseGroup(testCtx, srcType.name, "")); for (const auto& poolStrategy: poolStrategies) { GroupPtr poolStrategyGroup(new tcu::TestCaseGroup(testCtx, poolStrategy.name, "")); for (const auto& moment : updateMoments) { //if (moment.updateMoment == UpdateMoment::UPDATE_AFTER_BIND && srcType.sourceSetType == SourceSetType::HOST_ONLY) // continue; if (moment.updateMoment == UpdateMoment::UPDATE_AFTER_BIND && hasInputAttachments) continue; GroupPtr momentGroup(new tcu::TestCaseGroup(testCtx, moment.name, "")); for (const auto& accessType : arrayAccessTypes) { // Skip combinations that make no sense. if (hasArrays && accessType.arrayAccessType == ArrayAccessType::NO_ARRAY) continue; if (!hasArrays && accessType.arrayAccessType != ArrayAccessType::NO_ARRAY) continue; GroupPtr accessTypeGroup(new tcu::TestCaseGroup(testCtx, accessType.name, "")); for (const auto& testStage : stagesToTest) { const auto beginItr = std::begin(testStageList); const auto endItr = std::end(testStageList); const auto iter = std::find_if(beginItr, endItr, [testStage] (const StageAndName& ts) { return ts.testingStage == testStage; }); DE_ASSERT(iter != endItr); const auto& stage = *iter; if (hasInputAttachments && stage.testingStage != TestingStage::FRAGMENT) continue; TestParams params = { descriptorSet, ut.updateType, srcStrategy.sourceSetStrategy, srcType.sourceSetType, poolStrategy.poolMutableStrategy, moment.updateMoment, accessType.arrayAccessType, stage.testingStage, }; accessTypeGroup->addChild(new MutableTypesTest(testCtx, stage.name, "", params)); } momentGroup->addChild(accessTypeGroup.release()); } poolStrategyGroup->addChild(momentGroup.release()); } srcTypeGroup->addChild(poolStrategyGroup.release()); } srcStrategyGroup->addChild(srcTypeGroup.release()); } updateGroup->addChild(srcStrategyGroup.release()); } parentGroup->addChild(updateGroup.release()); } } } std::string descriptorTypeStr (VkDescriptorType descriptorType) { static const auto prefixLen = std::string("VK_DESCRIPTOR_TYPE_").size(); return de::toLower(de::toString(descriptorType).substr(prefixLen)); } tcu::TestCaseGroup* createDescriptorValveMutableTests (tcu::TestContext& testCtx) { GroupPtr mainGroup(new tcu::TestCaseGroup(testCtx, "mutable_descriptor", "Tests for VK_VALVE_mutable_descriptor_type")); const VkDescriptorType basicDescriptorTypes[] = { VK_DESCRIPTOR_TYPE_SAMPLER, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, }; static const auto mandatoryTypes = getMandatoryMutableTypes(); using StageVec = std::vector; const StageVec allStages = { TestingStage::COMPUTE, TestingStage::VERTEX, TestingStage::TESS_CONTROL, TestingStage::TESS_EVAL, TestingStage::GEOMETRY, TestingStage::FRAGMENT, TestingStage::RAY_GEN, TestingStage::INTERSECTION, TestingStage::ANY_HIT, TestingStage::CLOSEST_HIT, TestingStage::MISS, TestingStage::CALLABLE, }; const StageVec reducedStages = { TestingStage::COMPUTE, TestingStage::VERTEX, TestingStage::FRAGMENT, TestingStage::RAY_GEN, }; const StageVec computeOnly = { TestingStage::COMPUTE, }; // Basic tests with a single mutable descriptor. { GroupPtr singleCases(new tcu::TestCaseGroup(testCtx, "single", "Basic mutable descriptor tests with a single mutable descriptor")); for (const auto& descriptorType : basicDescriptorTypes) { const auto groupName = descriptorTypeStr(descriptorType); const std::vector actualTypes(1u, descriptorType); DescriptorSetPtr setPtr; { DescriptorSet::BindingPtrVector setBindings; setBindings.emplace_back(new SingleBinding(VK_DESCRIPTOR_TYPE_MUTABLE_VALVE, actualTypes)); setPtr = DescriptorSetPtr(new DescriptorSet(setBindings)); } GroupPtr subGroup(new tcu::TestCaseGroup(testCtx, groupName.c_str(), "")); createMutableTestVariants(testCtx, subGroup.get(), setPtr, allStages); singleCases->addChild(subGroup.release()); } // Case with a single descriptor that iterates several types. { DescriptorSetPtr setPtr; { DescriptorSet::BindingPtrVector setBindings; setBindings.emplace_back(new SingleBinding(VK_DESCRIPTOR_TYPE_MUTABLE_VALVE, mandatoryTypes)); setPtr = DescriptorSetPtr(new DescriptorSet(setBindings)); } GroupPtr subGroup(new tcu::TestCaseGroup(testCtx, "all_mandatory", "")); createMutableTestVariants(testCtx, subGroup.get(), setPtr, reducedStages); singleCases->addChild(subGroup.release()); } // Cases that try to verify switching from any descriptor type to any other is possible. { GroupPtr subGroup(new tcu::TestCaseGroup(testCtx, "switches", "Test switching from one to another descriptor type works as expected")); for (const auto& initialDescriptorType : basicDescriptorTypes) { for (const auto& finalDescriptorType : basicDescriptorTypes) { if (initialDescriptorType == finalDescriptorType) continue; const std::vector mutableTypes { initialDescriptorType, finalDescriptorType }; DescriptorSet::BindingPtrVector setBindings; setBindings.emplace_back(new SingleBinding(VK_DESCRIPTOR_TYPE_MUTABLE_VALVE, mutableTypes)); DescriptorSetPtr setPtr = DescriptorSetPtr(new DescriptorSet(setBindings)); const auto groupName = descriptorTypeStr(initialDescriptorType) + "_" + descriptorTypeStr(finalDescriptorType); GroupPtr combinationGroup(new tcu::TestCaseGroup(testCtx, groupName.c_str(), "")); createMutableTestVariants(testCtx, combinationGroup.get(), setPtr, reducedStages); subGroup->addChild(combinationGroup.release()); } } singleCases->addChild(subGroup.release()); } mainGroup->addChild(singleCases.release()); } // Cases with a single non-mutable descriptor. This provides some basic checks to verify copying to non-mutable bindings works. { GroupPtr singleNonMutableGroup (new tcu::TestCaseGroup(testCtx, "single_nonmutable", "Tests using a single non-mutable descriptor")); for (const auto& descriptorType : basicDescriptorTypes) { DescriptorSet::BindingPtrVector bindings; bindings.emplace_back(new SingleBinding(descriptorType, std::vector())); DescriptorSetPtr descriptorSet (new DescriptorSet(bindings)); const auto groupName = descriptorTypeStr(descriptorType); GroupPtr descGroup (new tcu::TestCaseGroup(testCtx, groupName.c_str(), "")); createMutableTestVariants(testCtx, descGroup.get(), descriptorSet, reducedStages); singleNonMutableGroup->addChild(descGroup.release()); } mainGroup->addChild(singleNonMutableGroup.release()); } const struct { bool unbounded; const char* name; } unboundedCases[] = { {false, "constant_size"}, {true, "unbounded"}, }; const struct { bool aliasing; const char* name; } aliasingCases[] = { {false, "noaliasing"}, {true, "aliasing"}, }; const struct { bool oneArrayOnly; bool mixNonMutable; const char* groupName; const char* groupDesc; } arrayCountGroups[] = { {true, false, "one_array", "Tests using an array of mutable descriptors"}, {false, false, "multiple_arrays", "Tests using multiple arrays of mutable descriptors"}, {false, true, "multiple_arrays_mixed", "Tests using multiple arrays of mutable descriptors mixed with arrays of nonmutable ones"}, }; for (const auto& variant : arrayCountGroups) { GroupPtr arrayGroup(new tcu::TestCaseGroup(testCtx, variant.groupName, variant.groupDesc)); for (const auto& unboundedCase : unboundedCases) { GroupPtr unboundedGroup(new tcu::TestCaseGroup(testCtx, unboundedCase.name, "")); for (const auto& aliasingCase : aliasingCases) { GroupPtr aliasingGroup(new tcu::TestCaseGroup(testCtx, aliasingCase.name, "")); DescriptorSet::BindingPtrVector setBindings; // Prepare descriptors for this test variant. for (size_t mandatoryTypesRotation = 0; mandatoryTypesRotation < mandatoryTypes.size(); ++mandatoryTypesRotation) { const bool isLastBinding = (variant.oneArrayOnly || mandatoryTypesRotation == mandatoryTypes.size() - 1u); const bool isUnbounded = (unboundedCase.unbounded && isLastBinding); // Create a rotation of the mandatory types for each mutable array binding. auto mandatoryTypesVector = mandatoryTypes; { const auto beginPtr = &mandatoryTypesVector[0]; const auto endPtr = beginPtr + mandatoryTypesVector.size(); std::rotate(beginPtr, &mandatoryTypesVector[mandatoryTypesRotation], endPtr); } std::vector arrayBindings; if (aliasingCase.aliasing) { // With aliasing, the descriptor types rotate in each descriptor. for (size_t typeIdx = 0; typeIdx < mandatoryTypesVector.size(); ++typeIdx) { auto rotatedTypes = mandatoryTypesVector; const auto beginPtr = &rotatedTypes[0]; const auto endPtr = beginPtr + rotatedTypes.size(); std::rotate(beginPtr, &rotatedTypes[typeIdx], endPtr); arrayBindings.emplace_back(VK_DESCRIPTOR_TYPE_MUTABLE_VALVE, rotatedTypes); } } else { // Without aliasing, all descriptors use the same type at the same time. const SingleBinding noAliasingBinding(VK_DESCRIPTOR_TYPE_MUTABLE_VALVE, mandatoryTypesVector); arrayBindings.resize(mandatoryTypesVector.size(), noAliasingBinding); } setBindings.emplace_back(new ArrayBinding(isUnbounded, arrayBindings)); if (variant.mixNonMutable && !isUnbounded) { // Create a non-mutable array binding interleaved with the other ones. const SingleBinding nonMutableBinding(mandatoryTypes[mandatoryTypesRotation], std::vector()); std::vector nonMutableBindings(mandatoryTypes.size(), nonMutableBinding); setBindings.emplace_back(new ArrayBinding(false, nonMutableBindings)); } if (variant.oneArrayOnly) break; } DescriptorSetPtr descriptorSet(new DescriptorSet(setBindings)); createMutableTestVariants(testCtx, aliasingGroup.get(), descriptorSet, computeOnly); unboundedGroup->addChild(aliasingGroup.release()); } arrayGroup->addChild(unboundedGroup.release()); } mainGroup->addChild(arrayGroup.release()); } // Cases with a single mutable binding followed by an array of mutable bindings. // The array will use a single type beyond the mandatory ones. { GroupPtr singleAndArrayGroup(new tcu::TestCaseGroup(testCtx, "single_and_array", "Tests using a single mutable binding followed by a mutable array binding")); for (const auto& descriptorType : basicDescriptorTypes) { // Input attachments will not use arrays. if (descriptorType == VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT) continue; if (de::contains(begin(mandatoryTypes), end(mandatoryTypes), descriptorType)) continue; const auto groupName = descriptorTypeStr(descriptorType); GroupPtr descTypeGroup(new tcu::TestCaseGroup(testCtx, groupName.c_str(), "")); for (const auto& aliasingCase : aliasingCases) { GroupPtr aliasingGroup(new tcu::TestCaseGroup(testCtx, aliasingCase.name, "")); DescriptorSet::BindingPtrVector setBindings; std::vector arrayBindings; // Single mutable descriptor as the first binding. setBindings.emplace_back(new SingleBinding(VK_DESCRIPTOR_TYPE_MUTABLE_VALVE, mandatoryTypes)); // Descriptor array as the second binding. auto arrayBindingDescTypes = mandatoryTypes; arrayBindingDescTypes.push_back(descriptorType); if (aliasingCase.aliasing) { // With aliasing, the descriptor types rotate in each descriptor. for (size_t typeIdx = 0; typeIdx < arrayBindingDescTypes.size(); ++typeIdx) { auto rotatedTypes = arrayBindingDescTypes; const auto beginPtr = &rotatedTypes[0]; const auto endPtr = beginPtr + rotatedTypes.size(); std::rotate(beginPtr, &rotatedTypes[typeIdx], endPtr); arrayBindings.emplace_back(VK_DESCRIPTOR_TYPE_MUTABLE_VALVE, rotatedTypes); } } else { // Without aliasing, all descriptors use the same type at the same time. const SingleBinding noAliasingBinding(VK_DESCRIPTOR_TYPE_MUTABLE_VALVE, arrayBindingDescTypes); arrayBindings.resize(arrayBindingDescTypes.size(), noAliasingBinding); } // Second binding: array binding. setBindings.emplace_back(new ArrayBinding(false/*unbounded*/, arrayBindings)); // Create set and test variants. DescriptorSetPtr descriptorSet(new DescriptorSet(setBindings)); createMutableTestVariants(testCtx, aliasingGroup.get(), descriptorSet, computeOnly); descTypeGroup->addChild(aliasingGroup.release()); } singleAndArrayGroup->addChild(descTypeGroup.release()); } mainGroup->addChild(singleAndArrayGroup.release()); } // Cases with several mutable non-array bindings. { GroupPtr multipleGroup (new tcu::TestCaseGroup(testCtx, "multiple", "Tests using multiple mutable bindings")); GroupPtr mutableOnlyGroup (new tcu::TestCaseGroup(testCtx, "mutable_only", "Tests using only mutable descriptors")); GroupPtr mixedGroup (new tcu::TestCaseGroup(testCtx, "mixed", "Tests mixing mutable descriptors an non-mutable descriptors")); // Each descriptor will have a different type in every iteration, like in the one_array aliasing case. for (int groupIdx = 0; groupIdx < 2; ++groupIdx) { const bool mixed = (groupIdx == 1); DescriptorSet::BindingPtrVector setBindings; for (size_t typeIdx = 0; typeIdx < mandatoryTypes.size(); ++typeIdx) { auto rotatedTypes = mandatoryTypes; const auto beginPtr = &rotatedTypes[0]; const auto endPtr = beginPtr + rotatedTypes.size(); std::rotate(beginPtr, &rotatedTypes[typeIdx], endPtr); setBindings.emplace_back(new SingleBinding(VK_DESCRIPTOR_TYPE_MUTABLE_VALVE, rotatedTypes)); // Additional non-mutable binding interleaved with the mutable ones. if (mixed) setBindings.emplace_back(new SingleBinding(rotatedTypes[0], std::vector())); } DescriptorSetPtr descriptorSet(new DescriptorSet(setBindings)); const auto dstGroup = (mixed ? mixedGroup.get() : mutableOnlyGroup.get()); createMutableTestVariants(testCtx, dstGroup, descriptorSet, computeOnly); } multipleGroup->addChild(mutableOnlyGroup.release()); multipleGroup->addChild(mixedGroup.release()); mainGroup->addChild(multipleGroup.release()); } return mainGroup.release(); } } // BindingModel } // vkt