blob: b268b931caf634842a6353361ab5d8760b6b2535 [file] [log] [blame]
/*-------------------------------------------------------------------------
* 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 <vector>
#include <algorithm>
#include <iterator>
#include <set>
#include <sstream>
#include <limits>
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<deUint16>(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<VkDescriptorType> getForbiddenMutableTypes ()
{
return std::vector<VkDescriptorType>
{
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<VkDescriptorType> getMandatoryMutableTypes ()
{
return std::vector<VkDescriptorType>
{
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<VkDescriptorType>& types)
{
DescriptorTypeFlags result = 0u;
for (const auto& t : types)
result |= toDescriptorTypeFlagBit(t);
return result;
}
// Convert bitfield to vector of descriptor types.
std::vector<VkDescriptorType> toDescriptorTypeVector (DescriptorTypeFlags bitfield)
{
std::vector<VkDescriptorType> 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<VkSampler> 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<ImageWithMemory> 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<ImageWithMemory>(new ImageWithMemory(vkd, device, alloc, imageCreateInfo, MemoryRequirement::Any));
}
Move<VkImageView> 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<BufferWithMemory> 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<VkDeviceSize>(sizeof(deUint32) * static_cast<size_t>(numElements));
const auto bufferCreateInfo = makeBufferCreateInfo(bufferSize, bufferUsage);
return de::MovePtr<BufferWithMemory>(new BufferWithMemory(vkd, device, alloc, bufferCreateInfo, MemoryRequirement::HostVisible));
}
Move<VkBufferView> makeDefaultBufferView (const DeviceInterface& vkd, VkDevice device, VkBuffer buffer)
{
const auto bufferOffset = static_cast<VkDeviceSize>(0);
const auto bufferSize = static_cast<VkDeviceSize>(sizeof(deUint32));
return makeBufferView(vkd, device, buffer, getDescriptorImageFormat(), bufferOffset, bufferSize);
}
struct AccelerationStructureData
{
using TLASPtr = de::MovePtr<TopLevelAccelerationStructure>;
using BLASPtr = de::MovePtr<BottomLevelAccelerationStructure>;
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<float>(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<tcu::Vec3> 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<BottomLevelAccelerationStructure> 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<VkSampler> sampler;
de::MovePtr<ImageWithMemory> imageWithMemory;
Move<VkImageView> imageView;
de::MovePtr<BufferWithMemory> bufferWithMemory;
Move<VkBufferView> 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<VkDeviceSize>(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, &copyRegion);
// 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<deUint32> 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<WriteInfo>;
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<VkDeviceSize>(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<deUint32> getStoredValue (const DeviceInterface& vkd, VkDevice device, Allocator& alloc, deUint32 qIndex, VkQueue queue, deUint32 position = 0u) const
{
if (position != 0u)
DE_ASSERT(static_cast<bool>(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<VkDeviceSize>(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, &copyRegion);
// 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<const char*>(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<size_t>(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<VkDescriptorType> 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<VkDescriptorType> 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<VkDescriptorType> 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<bool> 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<BindingInterface> toMutable (deUint32 iteration) const = 0;
virtual de::MovePtr<BindingInterface> toNonMutable (deUint32 iteration) const = 0;
// Create resources needed to back up this binding.
virtual std::vector<Resource> 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<deInt32> arraySize) const = 0;
// Get GLSL statements to check this binding.
virtual std::string glslCheckStatements (deUint32 iteration, deUint32 setNum, deUint32 bindingNum, deUint32 baseValue, tcu::Maybe<deUint32> 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<VkDescriptorType> mutableTypesVec; // The types that will be used for each iteration of a test if mutable.
public:
SingleBinding (VkDescriptorType type_, std::vector<VkDescriptorType> 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<deUint32>::max());
return static_cast<deUint32>(vecSize);
}
VkDescriptorType typeAtIteration (deUint32 iteration) const
{
return typesAtIteration(iteration)[0];
}
std::vector<VkDescriptorType> usedTypes () const
{
if (type != VK_DESCRIPTOR_TYPE_MUTABLE_VALVE)
return std::vector<VkDescriptorType>(1u, type);
return mutableTypesVec;
}
std::vector<VkDescriptorType> typesAtIteration (deUint32 iteration) const override
{
const auto typesVec = usedTypes();
return std::vector<VkDescriptorType>(1u, typesVec[static_cast<size_t>(iteration) % typesVec.size()]);
}
VkDescriptorType mainType () const override
{
return type;
}
std::vector<VkDescriptorType> 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<BindingInterface> 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<BindingInterface>(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<BindingInterface>(new SingleBinding(VK_DESCRIPTOR_TYPE_MUTABLE_VALVE, toDescriptorTypeVector(descFlags)));
}
de::MovePtr<BindingInterface> toNonMutable (deUint32 iteration) const override
{
return de::MovePtr<BindingInterface>(new SingleBinding(typeAtIteration(iteration), std::vector<VkDescriptorType>()));
}
std::vector<Resource> 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<Resource> 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<deInt32> arraySize) const override
{
const auto descriptorType = typeAtIteration(iteration);
const std::string arraySuffix = ((static_cast<bool>(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<deUint32> 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<SingleBinding> bindings;
public:
ArrayBinding (bool unbounded_, std::vector<SingleBinding> 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<VkDescriptorType> basicTypes;
std::set<DescriptorTypeFlags> 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<size_t> 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<deUint32>::max());
return static_cast<deUint32>(*maxElement);
}
std::vector<VkDescriptorType> typesAtIteration (deUint32 iteration) const override
{
std::vector<VkDescriptorType> 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<VkDescriptorType> 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<BindingInterface> 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<SingleBinding*>(firstBindingPtr.get());
const std::vector<SingleBinding> newBindings (bindings.size(), firstBinding);
return de::MovePtr<BindingInterface>(new ArrayBinding(unbounded, newBindings));
}
de::MovePtr<BindingInterface> 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<VkDescriptorType>());
const std::vector<SingleBinding> newBindings (bindings.size(), firstBinding);
return de::MovePtr<BindingInterface>(new ArrayBinding(unbounded, newBindings));
}
std::vector<Resource> createResources (
const DeviceInterface& vkd, VkDevice device, Allocator& alloc, deUint32 qIndex, VkQueue queue,
deUint32 iteration, bool useAABBs, deUint32 baseValue) const override
{
std::vector<Resource> resources;
const auto numBindings = static_cast<deUint32>(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<deInt32> arraySize) const override
{
const auto descriptorCount = bindings.size();
const auto arraySizeVal = (isUnbounded() ? tcu::just(deInt32{-1}) : tcu::just(static_cast<deInt32>(descriptorCount)));
DE_UNREF(arraySize);
DE_ASSERT(descriptorCount < static_cast<size_t>(std::numeric_limits<deInt32>::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<VkDescriptorType> 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<deUint32> arrayIndex, bool usePushConstants) const override
{
DE_ASSERT(!arrayIndex);
DE_UNREF(arrayIndex); // For release builds.
std::ostringstream checks;
const auto numDescriptors = static_cast<deUint32>(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<DescriptorSet>;
class DescriptorSet
{
public:
using BindingInterfacePtr = de::MovePtr<BindingInterface>;
using BindingPtrVector = std::vector<BindingInterfacePtr>;
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<deUint32> 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<VkDescriptorPool> makeDescriptorPool (const DeviceInterface& vkd, VkDevice device, PoolMutableStrategy strategy, VkDescriptorPoolCreateFlags flags) const
{
std::vector<VkDescriptorPoolSize> poolSizes;
std::vector<std::vector<VkDescriptorType>> mutableTypesVec;
std::vector<VkMutableDescriptorTypeListVALVE> 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<deUint32>(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<deUint32>(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<deUint32>(poolSizes.size());
poolCreateInfo.pPoolSizes = de::dataOrNull(poolSizes);
VkMutableDescriptorTypeCreateInfoVALVE mutableInfo = initVulkanStructure();
if (strategy == PoolMutableStrategy::KEEP_TYPES || strategy == PoolMutableStrategy::EXPAND_TYPES)
{
mutableInfo.mutableDescriptorTypeListCount = static_cast<deUint32>(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<VkDescriptorSetLayout> layout;
explicit DescriptorSetLayoutResult (Move<VkDescriptorSetLayout>&& 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<VkDescriptorSetLayoutBinding> bindingsVec;
std::vector<std::vector<VkDescriptorType>> mutableTypesVec;
std::vector<VkMutableDescriptorTypeListVALVE> 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<deUint32>(bindingIdx), // deUint32 binding;
mainType, // VkDescriptorType descriptorType;
static_cast<deUint32>(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<deUint32>(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<bool> 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<VkDescriptorSetLayoutBindingFlagsCreateInfo>;
using BindingFlagsVecPtr = de::MovePtr<std::vector<VkDescriptorBindingFlags>>;
FlagsCreateInfoPtr flagsCreateInfo;
BindingFlagsVecPtr bindingFlagsVec;
if (updateAfterBind || lastIsUnbounded || aliasingNeded)
{
flagsCreateInfo = FlagsCreateInfoPtr(new VkDescriptorSetLayoutBindingFlagsCreateInfo);
*flagsCreateInfo = initVulkanStructure();
bindingFlagsVec = BindingFlagsVecPtr(new std::vector<VkDescriptorBindingFlags>(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<deUint32>(bindingFlagsVec->size());
flagsCreateInfo->pBindingFlags = de::dataOrNull(*bindingFlagsVec);
}
const VkMutableDescriptorTypeCreateInfoVALVE createInfoValve = {
VK_STRUCTURE_TYPE_MUTABLE_DESCRIPTOR_TYPE_CREATE_INFO_VALVE,
flagsCreateInfo.get(),
static_cast<deUint32>(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<deUint32>(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<VkDescriptorSetLayout> 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<Resource> createResources (const DeviceInterface& vkd, VkDevice device, Allocator& alloc, deUint32 qIndex, VkQueue queue, deUint32 iteration, bool useAABBs) const
{
// Create resources for each binding.
std::vector<Resource> result;
result.reserve(numDescriptors());
const auto bindingsCount = static_cast<deUint32>(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<Resource>& resources) const
{
// Make sure the number of resources is correct.
const auto numResources = resources.size();
DE_ASSERT(numDescriptors() == numResources);
std::vector<VkWriteDescriptorSet> descriptorWrites;
descriptorWrites.reserve(numResources);
std::vector<VkDescriptorImageInfo> imageInfoVec;
std::vector<VkDescriptorBufferInfo> bufferInfoVec;
std::vector<VkBufferView> bufferViewVec;
std::vector<VkWriteDescriptorSetAccelerationStructureKHR> 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<deUint32>(bindingIdx),
static_cast<deUint32>(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<deUint32>(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<VkCopyDescriptorSet> copies;
for (size_t bindingIdx = 0; bindingIdx < numBindings(); ++bindingIdx)
{
const auto& binding = getBinding(bindingIdx);
const auto bindingNumber = static_cast<deUint32>(bindingIdx);
const auto descriptorCount = static_cast<deUint32>(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<deUint32>(copies.size()), de::dataOrNull(copies));
}
// Does any binding in the set need aliasing in a given iteration?
bool needsAliasing (deUint32 iteration) const
{
std::vector<bool> 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<bool> 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<deUint32> getVariableDescriptorCount () const
{
if (lastBindingIsUnbounded())
return tcu::just(static_cast<deUint32>(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<deUint32>::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<size_t>(std::numeric_limits<deInt32>::max()));
const auto arraySize = (isArray ? (isUnbounded ? tcu::just(deInt32{-1}) : tcu::just(static_cast<deInt32>(bindingSize)))
: tcu::Nothing);
shader << binding->glslDeclarations(iter, 0u, static_cast<deUint32>(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<deUint32>(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<VkDescriptorType> getUsedDescriptorTypes (const DescriptorSet& descriptorSet, deUint32 numIterations, bool (*predicate)(const BindingInterface* binding))
{
std::set<VkDescriptorType> 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<VkDescriptorType> getAllUsedDescriptorTypes (const DescriptorSet& descriptorSet, deUint32 numIterations)
{
return getUsedDescriptorTypes(descriptorSet, numIterations, [] (const BindingInterface*) { return true; });
}
std::set<VkDescriptorType> 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<VkRenderPass> buildRenderPass (const DeviceInterface& vkd, VkDevice device, const std::vector<Resource>& resources)
{
const auto imageFormat = getDescriptorImageFormat();
std::vector<VkAttachmentDescription> attachmentDescriptions;
std::vector<VkAttachmentReference> attachmentReferences;
std::vector<deUint32> attachmentIndices;
for (const auto& resource : resources)
{
if (resource.descriptorType == VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT)
{
const auto nextIndex = static_cast<deUint32>(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<deUint32>(attachmentDescriptions.size());
DE_ASSERT(attachmentCount == static_cast<deUint32>(attachmentIndices.size()));
DE_ASSERT(attachmentCount == static_cast<deUint32>(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<deUint32>(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<VkPipeline> 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<VkViewport> viewports (1u, makeViewport(extent));
const std::vector<VkRect2D> 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<deUint32>(viewports.size()), // deUint32 viewportCount;
de::dataOrNull(viewports), // const VkViewport* pViewports;
static_cast<deUint32>(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<VkFramebuffer> buildFramebuffer (const DeviceInterface& vkd, VkDevice device, VkRenderPass renderPass, const std::vector<Resource>& resources)
{
const auto extent = getDefaultExtent();
std::vector<VkImageView> 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<deUint32>(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<std::vector<Resource>> 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<VkDescriptorSetVariableDescriptorCountAllocateInfo>;
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<VkDescriptorPool> srcPool;
Move<VkDescriptorSetLayout> srcLayout;
Move<VkDescriptorSet> srcSet;
// Extra set for external resources and output buffer.
std::vector<Resource> 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<VkDescriptorPool> 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<VkDescriptorSetLayout> 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<VkDescriptorBufferInfo>;
using DescriptorImageInfoPtr = de::MovePtr<VkDescriptorImageInfo>;
using DescriptorASInfoPtr = de::MovePtr<VkWriteDescriptorSetAccelerationStructureKHR>;
deUint32 bindingCount = 0u;
DescriptorBufferInfoPtr bufferInfoPtr;
DescriptorImageInfoPtr imageInfoPtr;
DescriptorImageInfoPtr samplerInfoPtr;
DescriptorASInfoPtr asWriteInfoPtr;
const auto outputBufferSize = static_cast<VkDeviceSize>(sizeof(deUint32) * static_cast<size_t>(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<deUint32>(sizeof(zero)) /*size*/ };
// Needed for some test variants.
Move<VkShaderModule> vertPassthrough;
Move<VkShaderModule> tesePassthrough;
Move<VkShaderModule> tescPassthrough;
Move<VkShaderModule> rgenPassthrough;
Move<VkShaderModule> 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<VkDescriptorSet>();
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<VkDescriptorSetLayout> setLayouts = {dstLayout.get(), extraLayout.get()};
const std::vector<VkDescriptorSet> 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<deUint32>(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<VkPipeline> pipeline;
Move<VkRenderPass> renderPass;
Move<VkFramebuffer> framebuffer;
deUint32 shaderGroupHandleSize = 0u;
deUint32 shaderGroupBaseAlignment = 1u;
de::MovePtr<BufferWithMemory> raygenSBT;
de::MovePtr<BufferWithMemory> missSBT;
de::MovePtr<BufferWithMemory> hitSBT;
de::MovePtr<BufferWithMemory> 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<RayTracingPipeline>();
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<deUint32>(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<deUint32>(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<bool>(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<bool>(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<tcu::TestCaseGroup>;
void createMutableTestVariants (tcu::TestContext& testCtx, tcu::TestCaseGroup* parentGroup, const DescriptorSetPtr& descriptorSet, const std::vector<TestingStage>& 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<TestingStage>;
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<VkDescriptorType> 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<VkDescriptorType> 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<VkDescriptorType>()));
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<SingleBinding> 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<VkDescriptorType>());
std::vector<SingleBinding> 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<SingleBinding> 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<VkDescriptorType>()));
}
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