blob: fe67a516da473cccdf7be629e4b90f633d9c340a [file] [log] [blame]
/*------------------------------------------------------------------------
* Vulkan Conformance Tests
* ------------------------
*
* Copyright (c) 2021 The Khronos Group Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*//*!
* \file
* \brief Tests Verifying Graphics Pipeline Libraries
*//*--------------------------------------------------------------------*/
#include "vktPipelineLibraryTests.hpp"
#include "vkDefs.hpp"
#include "vkCmdUtil.hpp"
#include "vkObjUtil.hpp"
#include "vkTypeUtil.hpp"
#include "vkQueryUtil.hpp"
#include "vkImageUtil.hpp"
#include "vkBarrierUtil.hpp"
#include "vkBufferWithMemory.hpp"
#include "vkImageWithMemory.hpp"
#include "vkBuilderUtil.hpp"
#include "vktTestCase.hpp"
#include "vktTestGroupUtil.hpp"
#include "tcuCommandLine.hpp"
#include "tcuImageCompare.hpp"
#include "tcuTestLog.hpp"
#include "tcuRGBA.hpp"
#include "../draw/vktDrawCreateInfoUtil.hpp"
#include "deMath.h"
#include <vector>
#include <chrono>
#include <set>
namespace vkt
{
namespace pipeline
{
namespace
{
using namespace vk;
using namespace vkt;
using namespace tcu;
static const deUint32 RENDER_SIZE_WIDTH = 16u;
static const deUint32 RENDER_SIZE_HEIGHT = 16u;
static const VkColorComponentFlags COLOR_COMPONENTS_NO_RED = VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
static const VkGraphicsPipelineLibraryFlagBitsEXT GRAPHICS_PIPELINE_LIBRARY_FLAGS[] =
{
VK_GRAPHICS_PIPELINE_LIBRARY_VERTEX_INPUT_INTERFACE_BIT_EXT,
VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT,
VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT,
VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT,
};
static const VkGraphicsPipelineLibraryFlagsEXT ALL_GRAPHICS_PIPELINE_LIBRARY_FLAGS = static_cast<VkGraphicsPipelineLibraryFlagsEXT>(VK_GRAPHICS_PIPELINE_LIBRARY_VERTEX_INPUT_INTERFACE_BIT_EXT)
| static_cast<VkGraphicsPipelineLibraryFlagsEXT>(VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT)
| static_cast<VkGraphicsPipelineLibraryFlagsEXT>(VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT)
| static_cast<VkGraphicsPipelineLibraryFlagsEXT>(VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT);
struct PipelineTreeNode
{
deInt32 parentIndex;
deUint32 shaderCount;
};
typedef std::vector<PipelineTreeNode> PipelineTreeConfiguration;
struct TestParams
{
PipelineTreeConfiguration pipelineTreeConfiguration;
bool optimize;
bool delayedShaderCreate;
};
struct RuntimePipelineTreeNode
{
deInt32 parentIndex;
VkGraphicsPipelineLibraryFlagsEXT graphicsPipelineLibraryFlags;
VkGraphicsPipelineLibraryFlagsEXT subtreeGraphicsPipelineLibraryFlags;
Move<VkPipeline> pipeline;
std::vector<VkPipeline> pipelineLibraries;
};
typedef std::vector<RuntimePipelineTreeNode> RuntimePipelineTreeConfiguration;
inline UVec4 ivec2uvec (const IVec4& ivec)
{
return UVec4
{
static_cast<deUint32>(ivec[0]),
static_cast<deUint32>(ivec[1]),
static_cast<deUint32>(ivec[2]),
static_cast<deUint32>(ivec[3]),
};
}
inline std::string getTestName (const PipelineTreeConfiguration& pipelineTreeConfiguration)
{
std::string result;
int level = pipelineTreeConfiguration[0].parentIndex;
for (const auto& node: pipelineTreeConfiguration)
{
if (level != node.parentIndex)
{
DE_ASSERT(level < node.parentIndex);
result += '_';
level = node.parentIndex;
}
result += de::toString(node.shaderCount);
}
return result;
}
inline VkPipelineCreateFlags calcPipelineCreateFlags (bool optimize, bool buildLibrary)
{
VkPipelineCreateFlags result = 0;
if (buildLibrary)
result |= static_cast<VkPipelineCreateFlags>(VK_PIPELINE_CREATE_LIBRARY_BIT_KHR);
if (optimize)
{
if (buildLibrary)
result |= static_cast<VkPipelineCreateFlags>(VK_PIPELINE_CREATE_RETAIN_LINK_TIME_OPTIMIZATION_INFO_BIT_EXT);
else
result |= static_cast<VkPipelineCreateFlags>(VK_PIPELINE_CREATE_LINK_TIME_OPTIMIZATION_BIT_EXT);
}
return result;
}
inline VkRenderPass getRenderPass (VkGraphicsPipelineLibraryFlagsEXT subset, VkRenderPass renderPass)
{
static const VkGraphicsPipelineLibraryFlagsEXT subsetRequiresRenderPass = static_cast<VkGraphicsPipelineLibraryFlagsEXT>(VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT)
| static_cast<VkGraphicsPipelineLibraryFlagsEXT>(VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT)
| static_cast<VkGraphicsPipelineLibraryFlagsEXT>(VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT);
if ((subsetRequiresRenderPass & subset) != 0)
return renderPass;
return DE_NULL;
}
inline VkGraphicsPipelineLibraryCreateInfoEXT makeGraphicsPipelineLibraryCreateInfo (const VkGraphicsPipelineLibraryFlagsEXT flags)
{
const VkGraphicsPipelineLibraryCreateInfoEXT graphicsPipelineLibraryCreateInfo =
{
VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_LIBRARY_CREATE_INFO_EXT, // VkStructureType sType;
DE_NULL, // void* pNext;
flags, // VkGraphicsPipelineLibraryFlagsEXT flags;
};
return graphicsPipelineLibraryCreateInfo;
}
inline VkPipelineLibraryCreateInfoKHR makePipelineLibraryCreateInfo (const std::vector<VkPipeline>& pipelineLibraries)
{
const deUint32 libraryCount = static_cast<deUint32>(pipelineLibraries.size());
const VkPipeline* libraries = de::dataOrNull(pipelineLibraries);
const VkPipelineLibraryCreateInfoKHR pipelineLibraryCreateInfo =
{
VK_STRUCTURE_TYPE_PIPELINE_LIBRARY_CREATE_INFO_KHR, // VkStructureType sType;
DE_NULL, // const void* pNext;
libraryCount, // deUint32 libraryCount;
libraries, // const VkPipeline* pLibraries;
};
return pipelineLibraryCreateInfo;
}
inline std::string getGraphicsPipelineLibraryFlagsString (const VkGraphicsPipelineLibraryFlagsEXT flags)
{
std::string result;
if ((flags & VK_GRAPHICS_PIPELINE_LIBRARY_VERTEX_INPUT_INTERFACE_BIT_EXT) != 0) result += "VERTEX_INPUT_INTERFACE ";
if ((flags & VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT) != 0) result += "PRE_RASTERIZATION_SHADERS ";
if ((flags & VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT) != 0) result += "FRAGMENT_SHADER ";
if ((flags & VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT) != 0) result += "FRAGMENT_OUTPUT_INTERFACE ";
if (!result.empty())
result.resize(result.size() - 1);
return result;
};
VkImageCreateInfo makeColorImageCreateInfo (const VkFormat format, const deUint32 width, const deUint32 height)
{
const VkImageUsageFlags usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
const VkImageCreateInfo imageInfo =
{
VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
(VkImageCreateFlags)0, // VkImageCreateFlags flags;
VK_IMAGE_TYPE_2D, // VkImageType imageType;
format, // VkFormat format;
makeExtent3D(width, height, 1), // VkExtent3D extent;
1u, // deUint32 mipLevels;
1u, // deUint32 arrayLayers;
VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
usage, // VkImageUsageFlags usage;
VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
0u, // deUint32 queueFamilyIndexCount;
DE_NULL, // const deUint32* pQueueFamilyIndices;
VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
};
return imageInfo;
}
VkImageViewCreateInfo makeImageViewCreateInfo (VkImage image, VkFormat format, VkImageAspectFlags aspectMask)
{
const VkComponentMapping components =
{
VK_COMPONENT_SWIZZLE_R,
VK_COMPONENT_SWIZZLE_G,
VK_COMPONENT_SWIZZLE_B,
VK_COMPONENT_SWIZZLE_A,
};
const VkImageSubresourceRange subresourceRange =
{
aspectMask, // VkImageAspectFlags aspectMask;
0, // deUint32 baseMipLevel;
1, // deUint32 levelCount;
0, // deUint32 baseArrayLayer;
1, // deUint32 layerCount;
};
const VkImageViewCreateInfo result =
{
VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkImageViewCreateFlags flags;
image, // VkImage image;
VK_IMAGE_VIEW_TYPE_2D, // VkImageViewType viewType;
format, // VkFormat format;
components, // VkComponentMapping components;
subresourceRange, // VkImageSubresourceRange subresourceRange;
};
return result;
}
VkImageCreateInfo makeDepthImageCreateInfo (const VkFormat format, const deUint32 width, const deUint32 height)
{
const VkImageUsageFlags usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
const VkImageCreateInfo imageInfo =
{
VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
(VkImageCreateFlags)0, // VkImageCreateFlags flags;
VK_IMAGE_TYPE_2D, // VkImageType imageType;
format, // VkFormat format;
makeExtent3D(width, height, 1), // VkExtent3D extent;
1u, // deUint32 mipLevels;
1u, // deUint32 arrayLayers;
VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
usage, // VkImageUsageFlags usage;
VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
0u, // deUint32 queueFamilyIndexCount;
DE_NULL, // const deUint32* pQueueFamilyIndices;
VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
};
return imageInfo;
}
const VkFramebufferCreateInfo makeFramebufferCreateInfo (const VkRenderPass renderPass,
const deUint32 attachmentCount,
const VkImageView* attachments,
const deUint32 width,
const deUint32 height)
{
const VkFramebufferCreateInfo result =
{
VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0, // VkFramebufferCreateFlags flags;
renderPass, // VkRenderPass renderPass;
attachmentCount, // deUint32 attachmentCount;
attachments, // const VkImageView* pAttachments;
width, // deUint32 width;
height, // deUint32 height;
1, // deUint32 layers;
};
return result;
}
const VkPipelineMultisampleStateCreateInfo makePipelineMultisampleStateCreateInfo (void)
{
const VkPipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo =
{
VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkPipelineMultisampleStateCreateFlags flags;
VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits rasterizationSamples;
DE_FALSE, // VkBool32 sampleShadingEnable;
0.0f, // float minSampleShading;
DE_NULL, // const VkSampleMask* pSampleMask;
DE_FALSE, // VkBool32 alphaToCoverageEnable;
DE_FALSE, // VkBool32 alphaToOneEnable;
};
return pipelineMultisampleStateCreateInfo;
}
class GraphicsPipelineCreateInfo : public ::vkt::Draw::PipelineCreateInfo
{
public:
GraphicsPipelineCreateInfo (vk::VkPipelineLayout _layout,
vk::VkRenderPass _renderPass,
int _subpass,
vk::VkPipelineCreateFlags _flags)
: ::vkt::Draw::PipelineCreateInfo (_layout, _renderPass, _subpass, _flags)
, m_vertexInputBindingDescription ()
, m_vertexInputAttributeDescription ()
, m_shaderModuleCreateInfoCount (0)
, m_shaderModuleCreateInfo { initVulkanStructure(), initVulkanStructure() }
, m_pipelineShaderStageCreateInfo ()
, m_vertModule ()
, m_fragModule ()
{
}
VkVertexInputBindingDescription m_vertexInputBindingDescription;
VkVertexInputAttributeDescription m_vertexInputAttributeDescription;
deUint32 m_shaderModuleCreateInfoCount;
VkShaderModuleCreateInfo m_shaderModuleCreateInfo[2];
std::vector<VkPipelineShaderStageCreateInfo> m_pipelineShaderStageCreateInfo;
Move<VkShaderModule> m_vertModule;
Move<VkShaderModule> m_fragModule;
};
void updateVertexInputInterface (Context& context,
GraphicsPipelineCreateInfo& graphicsPipelineCreateInfo,
VkPrimitiveTopology topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
deUint32 vertexDescriptionCount = 1u)
{
DE_UNREF(context);
graphicsPipelineCreateInfo.m_vertexInputBindingDescription =
{
0u, // deUint32 binding;
sizeof(tcu::Vec4), // deUint32 strideInBytes;
VK_VERTEX_INPUT_RATE_VERTEX, // VkVertexInputRate inputRate;
};
graphicsPipelineCreateInfo.m_vertexInputAttributeDescription =
{
0u, // deUint32 location;
0u, // deUint32 binding;
VK_FORMAT_R32G32B32A32_SFLOAT, // VkFormat format;
0u // deUint32 offsetInBytes;
};
const VkPipelineVertexInputStateCreateInfo vertexInputStateCreateInfo
{
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkPipelineVertexInputStateCreateFlags flags;
vertexDescriptionCount, // deUint32 vertexBindingDescriptionCount;
&graphicsPipelineCreateInfo.m_vertexInputBindingDescription, // const VkVertexInputBindingDescription* pVertexBindingDescriptions;
vertexDescriptionCount, // deUint32 vertexAttributeDescriptionCount;
&graphicsPipelineCreateInfo.m_vertexInputAttributeDescription, // const VkVertexInputAttributeDescription* pVertexAttributeDescriptions;
};
const VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCreateInfo
{
VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkPipelineInputAssemblyStateCreateFlags flags;
topology, // VkPrimitiveTopology topology;
VK_FALSE, // VkBool32 primitiveRestartEnable;
};
graphicsPipelineCreateInfo.addState(vertexInputStateCreateInfo);
graphicsPipelineCreateInfo.addState(inputAssemblyStateCreateInfo);
}
void updatePreRasterization (Context& context,
GraphicsPipelineCreateInfo& graphicsPipelineCreateInfo,
bool delayedShaderCreate,
VkPolygonMode polygonMode = VK_POLYGON_MODE_FILL)
{
const ProgramBinary& shaderBinary = context.getBinaryCollection().get("vert");
VkShaderModuleCreateInfo& shaderModuleCreateInfo = graphicsPipelineCreateInfo.m_shaderModuleCreateInfo[graphicsPipelineCreateInfo.m_shaderModuleCreateInfoCount];
DE_ASSERT(graphicsPipelineCreateInfo.m_shaderModuleCreateInfoCount < DE_LENGTH_OF_ARRAY(graphicsPipelineCreateInfo.m_shaderModuleCreateInfo));
shaderModuleCreateInfo =
{
VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkShaderModuleCreateFlags flags;
(deUintptr)shaderBinary.getSize(), // deUintptr codeSize;
(deUint32*)shaderBinary.getBinary(), // const deUint32* pCode;
};
if (!delayedShaderCreate)
{
const DeviceInterface& vk = context.getDeviceInterface();
const VkDevice device = context.getDevice();
graphicsPipelineCreateInfo.m_vertModule = createShaderModule(vk, device, &shaderModuleCreateInfo);
}
const void* pNext = delayedShaderCreate
? &shaderModuleCreateInfo
: DE_NULL;
const VkShaderModule shaderModule = delayedShaderCreate
? DE_NULL
: *graphicsPipelineCreateInfo.m_vertModule;
const VkPipelineShaderStageCreateInfo pipelineShaderStageCreateInfo =
{
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // VkStructureType sType;
pNext, // const void* pNext;
0u, // VkPipelineShaderStageCreateFlags flags;
VK_SHADER_STAGE_VERTEX_BIT, // VkShaderStageFlagBits stage;
shaderModule, // VkShaderModule module;
"main", // const char* pName;
DE_NULL // const VkSpecializationInfo* pSpecializationInfo;
};
shaderBinary.setUsed();
// Within the VkPipelineLayout, all bindings that affect the specified shader stages
const VkViewport viewport = makeViewport(RENDER_SIZE_WIDTH, RENDER_SIZE_HEIGHT);
const VkRect2D scissor = makeRect2D(3 * RENDER_SIZE_WIDTH / 4, RENDER_SIZE_HEIGHT);
const VkPipelineViewportStateCreateInfo pipelineViewportStateCreateInfo =
{
VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkPipelineViewportStateCreateFlags flags;
1u, // deUint32 viewportCount;
&viewport, // const VkViewport* pViewports;
1u, // deUint32 scissorCount;
&scissor // const VkRect2D* pScissors;
};
const VkPipelineRasterizationStateCreateInfo pipelineRasterizationStateCreateInfo =
{
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkPipelineRasterizationStateCreateFlags flags;
VK_FALSE, // VkBool32 depthClampEnable;
VK_FALSE, // VkBool32 rasterizerDiscardEnable;
polygonMode, // VkPolygonMode polygonMode;
VK_CULL_MODE_NONE, // VkCullModeFlags cullMode;
VK_FRONT_FACE_COUNTER_CLOCKWISE, // VkFrontFace frontFace;
VK_FALSE, // VkBool32 depthBiasEnable;
0.0f, // float depthBiasConstantFactor;
0.0f, // float depthBiasClamp;
0.0f, // float depthBiasSlopeFactor;
1.0f, // float lineWidth;
};
graphicsPipelineCreateInfo.m_shaderModuleCreateInfoCount++;
graphicsPipelineCreateInfo.addShader(pipelineShaderStageCreateInfo);
graphicsPipelineCreateInfo.addState(pipelineViewportStateCreateInfo);
graphicsPipelineCreateInfo.addState(pipelineRasterizationStateCreateInfo);
}
void updatePostRasterization (Context& context,
GraphicsPipelineCreateInfo& graphicsPipelineCreateInfo,
bool delayedShaderCreate,
bool enableDepth = true)
{
const ProgramBinary& shaderBinary = context.getBinaryCollection().get("frag");
VkShaderModuleCreateInfo& shaderModuleCreateInfo = graphicsPipelineCreateInfo.m_shaderModuleCreateInfo[graphicsPipelineCreateInfo.m_shaderModuleCreateInfoCount];
DE_ASSERT(graphicsPipelineCreateInfo.m_shaderModuleCreateInfoCount < DE_LENGTH_OF_ARRAY(graphicsPipelineCreateInfo.m_shaderModuleCreateInfo));
shaderModuleCreateInfo =
{
VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkShaderModuleCreateFlags flags;
(deUintptr)shaderBinary.getSize(), // deUintptr codeSize;
(deUint32*)shaderBinary.getBinary(), // const deUint32* pCode;
};
if (!delayedShaderCreate)
{
const DeviceInterface& vk = context.getDeviceInterface();
const VkDevice device = context.getDevice();
graphicsPipelineCreateInfo.m_fragModule = createShaderModule(vk, device, &shaderModuleCreateInfo);
}
const void* pNext = delayedShaderCreate
? &shaderModuleCreateInfo
: DE_NULL;
const VkShaderModule shaderModule = delayedShaderCreate
? DE_NULL
: *graphicsPipelineCreateInfo.m_fragModule;
const VkPipelineShaderStageCreateInfo pipelineShaderStageCreateInfo =
{
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // VkStructureType sType;
pNext, // const void* pNext;
0u, // VkPipelineShaderStageCreateFlags flags;
VK_SHADER_STAGE_FRAGMENT_BIT, // VkShaderStageFlagBits stage;
shaderModule, // VkShaderModule module;
"main", // const char* pName;
DE_NULL // const VkSpecializationInfo* pSpecializationInfo;
};
shaderBinary.setUsed();
// Within the VkPipelineLayout, all bindings that affect the fragment shader stage
const VkPipelineDepthStencilStateCreateInfo pipelineDepthStencilStateCreateInfo =
{
VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkPipelineDepthStencilStateCreateFlags flags;
enableDepth, // VkBool32 depthTestEnable;
enableDepth, // VkBool32 depthWriteEnable;
VK_COMPARE_OP_LESS_OR_EQUAL, // VkCompareOp depthCompareOp;
VK_FALSE, // VkBool32 depthBoundsTestEnable;
VK_FALSE, // VkBool32 stencilTestEnable;
{ // VkStencilOpState front;
VK_STENCIL_OP_KEEP, // VkStencilOp failOp;
VK_STENCIL_OP_KEEP, // VkStencilOp passOp;
VK_STENCIL_OP_KEEP, // VkStencilOp depthFailOp;
VK_COMPARE_OP_NEVER, // VkCompareOp compareOp;
0u, // deUint32 compareMask;
0u, // deUint32 writeMask;
0u, // deUint32 reference;
},
{ // VkStencilOpState back;
VK_STENCIL_OP_KEEP, // VkStencilOp failOp;
VK_STENCIL_OP_KEEP, // VkStencilOp passOp;
VK_STENCIL_OP_KEEP, // VkStencilOp depthFailOp;
VK_COMPARE_OP_NEVER, // VkCompareOp compareOp;
0u, // deUint32 compareMask;
0u, // deUint32 writeMask;
0u, // deUint32 reference;
},
0.0f, // float minDepthBounds;
1.0f, // float maxDepthBounds;
};
graphicsPipelineCreateInfo.m_shaderModuleCreateInfoCount++;
graphicsPipelineCreateInfo.addShader(pipelineShaderStageCreateInfo);
DE_ASSERT(graphicsPipelineCreateInfo.pDepthStencilState == DE_NULL);
graphicsPipelineCreateInfo.addState(pipelineDepthStencilStateCreateInfo);
if (graphicsPipelineCreateInfo.pMultisampleState == DE_NULL)
{
const VkPipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo = makePipelineMultisampleStateCreateInfo();
graphicsPipelineCreateInfo.addState(pipelineMultisampleStateCreateInfo);
}
}
void updateFragmentOutputInterface (Context& context,
GraphicsPipelineCreateInfo& graphicsPipelineCreateInfo,
VkColorComponentFlags colorWriteMask = COLOR_COMPONENTS_NO_RED)
{
DE_UNREF(context);
// Number of blend attachments must equal the number of color attachments during any subpass.
const VkPipelineColorBlendAttachmentState pipelineColorBlendAttachmentState =
{
VK_FALSE, // VkBool32 blendEnable;
VK_BLEND_FACTOR_ONE, // VkBlendFactor srcColorBlendFactor;
VK_BLEND_FACTOR_ZERO, // VkBlendFactor dstColorBlendFactor;
VK_BLEND_OP_ADD, // VkBlendOp colorBlendOp;
VK_BLEND_FACTOR_ONE, // VkBlendFactor srcAlphaBlendFactor;
VK_BLEND_FACTOR_ZERO, // VkBlendFactor dstAlphaBlendFactor;
VK_BLEND_OP_ADD, // VkBlendOp alphaBlendOp;
colorWriteMask, // VkColorComponentFlags colorWriteMask;
};
const VkPipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo =
{
VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
(VkPipelineColorBlendStateCreateFlags)0, // VkPipelineColorBlendStateCreateFlags flags;
VK_FALSE, // VkBool32 logicOpEnable;
VK_LOGIC_OP_COPY, // VkLogicOp logicOp;
1u, // deUint32 attachmentCount;
&pipelineColorBlendAttachmentState, // const VkPipelineColorBlendAttachmentState* pAttachments;
{ 0.0f, 0.0f, 0.0f, 0.0f }, // float blendConstants[4];
};
graphicsPipelineCreateInfo.addState(pipelineColorBlendStateCreateInfo);
if (graphicsPipelineCreateInfo.pMultisampleState == DE_NULL)
{
const VkPipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo = makePipelineMultisampleStateCreateInfo();
graphicsPipelineCreateInfo.addState(pipelineMultisampleStateCreateInfo);
}
}
/*
To test that each of graphics pipeline libraries have influence on final pipeline
the functions have following features:
updateVertexInputInterface
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST
VK_VERTEX_INPUT_RATE_VERTEX
Z is read from uniform and written in shader
updatePreRasterization
VkRect2D scissor = makeRect2D(3 * RENDER_SIZE_WIDTH / 4, RENDER_SIZE_HEIGHT);
updatePostRasterization
Fragment shader top and bottom colors read from uniform buffer
updateFragmentOutputInterface
Cut off red component
*/
class PipelineLibraryTestInstance : public TestInstance
{
public:
PipelineLibraryTestInstance (Context& context,
const TestParams& data);
~PipelineLibraryTestInstance (void);
tcu::TestStatus iterate (void);
protected:
de::MovePtr<BufferWithMemory> makeVertexBuffer (void);
de::MovePtr<BufferWithMemory> makeZCoordBuffer (void);
de::MovePtr<BufferWithMemory> makePaletteBuffer (void);
Move<VkDescriptorPool> createDescriptorPool (void);
Move<VkDescriptorSetLayout> createDescriptorSetLayout (const VkBuffer vertShaderBuffer,
const VkBuffer fragShaderBuffer);
Move<VkDescriptorSet> createDescriptorSet (const VkDescriptorPool pool,
const VkDescriptorSetLayout layout,
const VkBuffer vertShaderBuffer,
const VkBuffer fragShaderBuffer);
bool verifyColorImage (const tcu::ConstPixelBufferAccess& pba);
bool verifyDepthImage (const tcu::ConstPixelBufferAccess& pba);
bool runTest (RuntimePipelineTreeConfiguration& runtimePipelineTreeConfiguration,
const bool optimize,
const bool delayedShaderCreate);
private:
TestParams m_data;
std::vector<tcu::Vec4> m_vertexData;
std::vector<tcu::Vec4> m_paletteData;
std::vector<tcu::Vec4> m_zCoordData;
};
PipelineLibraryTestInstance::PipelineLibraryTestInstance (Context& context, const TestParams& data)
: vkt::TestInstance (context)
, m_data (data)
, m_vertexData ()
, m_paletteData ()
{
m_vertexData =
{
{ -1.0f, -1.0f, 0.0f, 1.0f },
{ +1.0f, -1.0f, 0.5f, 1.0f },
{ -1.0f, +1.0f, 0.5f, 1.0f },
{ -1.0f, +1.0f, 0.5f, 1.0f },
{ +1.0f, -1.0f, 0.5f, 1.0f },
{ +1.0f, +1.0f, 1.0f, 1.0f },
};
m_paletteData =
{
{ 0.25f, 1.0f, 0.0f, 1.0f },
{ 0.75f, 0.0f, 1.0f, 1.0f },
};
m_zCoordData =
{
{ 0.25f, 0.75f, 0.0f, 1.0f },
};
}
PipelineLibraryTestInstance::~PipelineLibraryTestInstance (void)
{
}
de::MovePtr<BufferWithMemory> PipelineLibraryTestInstance::makeVertexBuffer (void)
{
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice device = m_context.getDevice();
Allocator& allocator = m_context.getDefaultAllocator();
const size_t bufferDataSize = de::dataSize(m_vertexData);
const VkBufferCreateInfo bufferCreateInfo = makeBufferCreateInfo(bufferDataSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
de::MovePtr<BufferWithMemory> buffer = de::MovePtr<BufferWithMemory>(new BufferWithMemory(vk, device, allocator, bufferCreateInfo, MemoryRequirement::HostVisible));
deMemcpy(buffer->getAllocation().getHostPtr(), m_vertexData.data(), bufferDataSize);
flushAlloc(vk, device, buffer->getAllocation());
return buffer;
}
de::MovePtr<BufferWithMemory> PipelineLibraryTestInstance::makeZCoordBuffer (void)
{
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice device = m_context.getDevice();
Allocator& allocator = m_context.getDefaultAllocator();
const size_t bufferDataSize = de::dataSize(m_zCoordData);
const VkBufferCreateInfo bufferCreateInfo = makeBufferCreateInfo(bufferDataSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT);
de::MovePtr<BufferWithMemory> buffer = de::MovePtr<BufferWithMemory>(new BufferWithMemory(vk, device, allocator, bufferCreateInfo, MemoryRequirement::HostVisible));
deMemcpy(buffer->getAllocation().getHostPtr(), m_zCoordData.data(), bufferDataSize);
flushAlloc(vk, device, buffer->getAllocation());
return buffer;
}
de::MovePtr<BufferWithMemory> PipelineLibraryTestInstance::makePaletteBuffer (void)
{
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice device = m_context.getDevice();
Allocator& allocator = m_context.getDefaultAllocator();
const size_t bufferDataSize = de::dataSize(m_paletteData);
const VkBufferCreateInfo bufferCreateInfo = makeBufferCreateInfo(bufferDataSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT);
de::MovePtr<BufferWithMemory> buffer = de::MovePtr<BufferWithMemory>(new BufferWithMemory(vk, device, allocator, bufferCreateInfo, MemoryRequirement::HostVisible));
deMemcpy(buffer->getAllocation().getHostPtr(), m_paletteData.data(), bufferDataSize);
flushAlloc(vk, device, buffer->getAllocation());
return buffer;
}
Move<VkDescriptorPool> PipelineLibraryTestInstance::createDescriptorPool (void)
{
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice device = m_context.getDevice();
return DescriptorPoolBuilder()
.addType(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 4)
.build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 3);
}
Move<VkDescriptorSetLayout> PipelineLibraryTestInstance::createDescriptorSetLayout (const VkBuffer vertShaderBuffer,
const VkBuffer fragShaderBuffer)
{
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice device = m_context.getDevice();
DescriptorSetLayoutBuilder builder;
if (vertShaderBuffer != DE_NULL)
builder.addIndexedBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1u, VK_SHADER_STAGE_VERTEX_BIT, 0u, DE_NULL);
if (fragShaderBuffer != DE_NULL)
builder.addIndexedBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1u, VK_SHADER_STAGE_FRAGMENT_BIT, 1u, DE_NULL);
return builder.build(vk, device);
}
Move<VkDescriptorSet> PipelineLibraryTestInstance::createDescriptorSet (const VkDescriptorPool pool,
const VkDescriptorSetLayout layout,
const VkBuffer vertShaderBuffer,
const VkBuffer fragShaderBuffer)
{
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice device = m_context.getDevice();
const VkDescriptorSetAllocateInfo allocInfo =
{
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
pool, // VkDescriptorPool descriptorPool;
1u, // deUint32 descriptorSetCount;
&layout // const VkDescriptorSetLayout* pSetLayouts;
};
Move<VkDescriptorSet> descriptorSet = allocateDescriptorSet(vk, device, &allocInfo);
DescriptorSetUpdateBuilder builder;
if (vertShaderBuffer != DE_NULL)
{
const VkDeviceSize vertShaderBufferSize = de::dataSize(m_zCoordData);
const VkDescriptorBufferInfo vertShaderBufferInfo = makeDescriptorBufferInfo(vertShaderBuffer, 0u, vertShaderBufferSize);
builder.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &vertShaderBufferInfo);
}
if (fragShaderBuffer != DE_NULL)
{
const VkDeviceSize fragShaderBufferSize = de::dataSize(m_paletteData);
const VkDescriptorBufferInfo fragShaderBufferInfo = makeDescriptorBufferInfo(fragShaderBuffer, 0u, fragShaderBufferSize);
builder.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(1u), VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &fragShaderBufferInfo);
}
builder.update(vk, device);
return descriptorSet;
}
bool PipelineLibraryTestInstance::runTest (RuntimePipelineTreeConfiguration& runtimePipelineTreeConfiguration,
const bool optimize,
const bool delayedShaderCreate)
{
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice device = m_context.getDevice();
const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
Allocator& allocator = m_context.getDefaultAllocator();
tcu::TestLog& log = m_context.getTestContext().getLog();
const VkFormat colorFormat = VK_FORMAT_R8G8B8A8_UNORM;
const VkFormat depthFormat = VK_FORMAT_D32_SFLOAT;
const VkGraphicsPipelineLibraryFlagsEXT vertPipelineFlags = static_cast<VkGraphicsPipelineLibraryFlagsEXT>(VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT);
const VkGraphicsPipelineLibraryFlagsEXT fragPipelineFlags = static_cast<VkGraphicsPipelineLibraryFlagsEXT>(VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT);
const VkGraphicsPipelineLibraryFlagsEXT samePipelineFlags = vertPipelineFlags | fragPipelineFlags;
const deInt32 nodeNdxLast = static_cast<deInt32>(runtimePipelineTreeConfiguration.size()) - 1;
const Move<VkRenderPass> renderPass = makeRenderPass(vk, device, colorFormat, depthFormat);
const de::MovePtr<BufferWithMemory> zCoordBuffer = makeZCoordBuffer();
const de::MovePtr<BufferWithMemory> paletteBuffer = makePaletteBuffer();
const Move<VkDescriptorPool> descriptorPool = createDescriptorPool();
const Move<VkDescriptorSetLayout> descriptorSetLayoutVert = createDescriptorSetLayout(**zCoordBuffer, DE_NULL);
const Move<VkDescriptorSetLayout> descriptorSetLayoutFrag = createDescriptorSetLayout(DE_NULL, **paletteBuffer);
const Move<VkDescriptorSetLayout> descriptorSetLayoutBoth = createDescriptorSetLayout(**zCoordBuffer, **paletteBuffer);
const Move<VkDescriptorSet> descriptorSetVert = createDescriptorSet(*descriptorPool, *descriptorSetLayoutVert, **zCoordBuffer, DE_NULL);
const Move<VkDescriptorSet> descriptorSetFrag = createDescriptorSet(*descriptorPool, *descriptorSetLayoutFrag, DE_NULL , **paletteBuffer);
VkDescriptorSet vecDescriptorSetBoth[2] = { *descriptorSetVert, *descriptorSetFrag };
VkDescriptorSetLayout vecLayoutVert[2] = { *descriptorSetLayoutVert, DE_NULL };
VkDescriptorSetLayout vecLayoutFrag[2] = { DE_NULL, *descriptorSetLayoutFrag };
VkDescriptorSetLayout vecLayoutBoth[2] = { *descriptorSetLayoutVert, *descriptorSetLayoutFrag };
VkPipelineLayoutCreateFlags pipelineLayoutCreateFlag = 0u;
if (m_data.delayedShaderCreate || (m_data.pipelineTreeConfiguration.size() > 1))
pipelineLayoutCreateFlag = VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT;
const Move<VkCommandPool> cmdPool = createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex);
const Move<VkCommandBuffer> cmdBuffer = allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
const Move<VkPipelineLayout> pipelineLayoutSame = makePipelineLayout(vk, device, 2, vecLayoutBoth, pipelineLayoutCreateFlag);
Move<VkPipelineLayout> pipelineLayoutVert;
Move<VkPipelineLayout> pipelineLayoutFrag;
Move<VkPipeline> rootPipeline;
// Go through tree nodes and create library for each up to root
for (deInt32 nodeNdx = nodeNdxLast; nodeNdx >= 0 ; --nodeNdx) // We expect only backward node reference, thus build pipielines from end is safe
{
RuntimePipelineTreeNode& node = runtimePipelineTreeConfiguration[nodeNdx];
const bool buildLibrary = (nodeNdx != 0);
const VkPipelineCreateFlags pipelineCreateFlags = calcPipelineCreateFlags(optimize, buildLibrary);
const VkGraphicsPipelineLibraryFlagsEXT subtreeGraphicsPipelineLibraryFlags = node.subtreeGraphicsPipelineLibraryFlags | node.graphicsPipelineLibraryFlags;
const bool samePipelineLayout = samePipelineFlags == (samePipelineFlags & subtreeGraphicsPipelineLibraryFlags);
const bool vertPipelineLayout = vertPipelineFlags == (vertPipelineFlags & subtreeGraphicsPipelineLibraryFlags);
const bool fragPipelineLayout = fragPipelineFlags == (fragPipelineFlags & subtreeGraphicsPipelineLibraryFlags);
if (samePipelineLayout)
; // pipelineLayoutSame is always built before.
else if (vertPipelineLayout)
{
if (!pipelineLayoutVert)
pipelineLayoutVert = makePipelineLayout(vk, device, 2, vecLayoutVert, pipelineLayoutCreateFlag);
}
else if (fragPipelineLayout)
{
if (!pipelineLayoutFrag)
pipelineLayoutFrag = makePipelineLayout(vk, device, 2, vecLayoutFrag, pipelineLayoutCreateFlag);
}
const VkPipelineLayout pipelineLayout = samePipelineLayout ? *pipelineLayoutSame
: vertPipelineLayout ? *pipelineLayoutVert
: fragPipelineLayout ? *pipelineLayoutFrag
: DE_NULL;
const VkRenderPass renderPassHandle = getRenderPass(node.graphicsPipelineLibraryFlags, *renderPass);
VkGraphicsPipelineLibraryCreateInfoEXT graphicsPipelineLibraryCreateInfo = makeGraphicsPipelineLibraryCreateInfo(node.graphicsPipelineLibraryFlags);
VkPipelineLibraryCreateInfoKHR linkingInfo = makePipelineLibraryCreateInfo(node.pipelineLibraries);
GraphicsPipelineCreateInfo graphicsPipelineCreateInfo (pipelineLayout, renderPassHandle, 0, pipelineCreateFlags);
for (const auto subsetFlag: GRAPHICS_PIPELINE_LIBRARY_FLAGS)
{
if ((node.graphicsPipelineLibraryFlags & subsetFlag) != 0)
{
switch (subsetFlag)
{
case VK_GRAPHICS_PIPELINE_LIBRARY_VERTEX_INPUT_INTERFACE_BIT_EXT: updateVertexInputInterface(m_context, graphicsPipelineCreateInfo); break;
case VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT: updatePreRasterization(m_context, graphicsPipelineCreateInfo, delayedShaderCreate); break;
case VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT: updatePostRasterization(m_context, graphicsPipelineCreateInfo, delayedShaderCreate);break;
case VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT: updateFragmentOutputInterface(m_context, graphicsPipelineCreateInfo); break;
default: TCU_THROW(InternalError, "Unknown pipeline subset");
}
}
}
if (graphicsPipelineLibraryCreateInfo.flags != ALL_GRAPHICS_PIPELINE_LIBRARY_FLAGS)
appendStructurePtrToVulkanChain(&graphicsPipelineCreateInfo.pNext, &graphicsPipelineLibraryCreateInfo);
if (linkingInfo.libraryCount != 0)
{
appendStructurePtrToVulkanChain(&graphicsPipelineCreateInfo.pNext, &linkingInfo);
graphicsPipelineCreateInfo.layout = *pipelineLayoutSame;
}
node.pipeline = createGraphicsPipeline(vk, device, DE_NULL, &graphicsPipelineCreateInfo);
if (buildLibrary)
{
DE_ASSERT(de::inBounds(node.parentIndex, 0, static_cast<deInt32>(runtimePipelineTreeConfiguration.size())));
runtimePipelineTreeConfiguration[node.parentIndex].pipelineLibraries.push_back(*node.pipeline);
}
else
{
DE_ASSERT(node.parentIndex == -1);
rootPipeline = node.pipeline;
}
}
// Queue commands and read results.
{
const tcu::UVec2 renderSize = { RENDER_SIZE_WIDTH, RENDER_SIZE_HEIGHT };
const VkRect2D renderArea = makeRect2D(renderSize.x(), renderSize.y());
const de::MovePtr<BufferWithMemory> vertexBuffer = makeVertexBuffer();
const deUint32 vertexCount = static_cast<deUint32>(m_vertexData.size());
const VkDeviceSize vertexBufferOffset = 0;
const Vec4 colorClearColor (0.0f, 0.0f, 0.0f, 1.0f);
const VkImageCreateInfo colorImageCreateInfo = makeColorImageCreateInfo(colorFormat, renderSize.x(), renderSize.y());
const ImageWithMemory colorImage (vk, device, allocator, colorImageCreateInfo, MemoryRequirement::Any);
const VkImageViewCreateInfo colorImageViewCreateInfo = makeImageViewCreateInfo(*colorImage, colorFormat, static_cast<VkImageAspectFlags>(VK_IMAGE_ASPECT_COLOR_BIT));
const Move<VkImageView> colorImageView = createImageView(vk, device, &colorImageViewCreateInfo);
const VkImageCreateInfo depthImageCreateInfo = makeDepthImageCreateInfo(depthFormat, renderSize.x(), renderSize.y());
const ImageWithMemory depthImage (vk, device, allocator, depthImageCreateInfo, MemoryRequirement::Any);
const VkImageViewCreateInfo depthImageViewCreateInfo = makeImageViewCreateInfo(*depthImage, depthFormat, static_cast<VkImageAspectFlags>(VK_IMAGE_ASPECT_DEPTH_BIT));
const Move<VkImageView> depthImageView = createImageView(vk, device, &depthImageViewCreateInfo);
const float depthClearDepth = 1.0f;
const deUint32 depthClearStencil = 0u;
const VkDeviceSize colorBufferDataSize = static_cast<VkDeviceSize>(renderSize.x() * renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat)));
const VkBufferCreateInfo colorBufferCreateInfo = makeBufferCreateInfo(colorBufferDataSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT);
const BufferWithMemory colorBuffer (vk, device, allocator, colorBufferCreateInfo, MemoryRequirement::HostVisible);
const VkDeviceSize depthBufferDataSize = static_cast<VkDeviceSize>(renderSize.x() * renderSize.y() * tcu::getPixelSize(mapVkFormat(depthFormat)));
const VkBufferCreateInfo depthBufferCreateInfo = makeBufferCreateInfo(depthBufferDataSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT);
const BufferWithMemory depthBuffer (vk, device, allocator, depthBufferCreateInfo, MemoryRequirement::HostVisible);
const VkImageView attachments[] = { *colorImageView, *depthImageView };
const VkFramebufferCreateInfo framebufferCreateInfo = makeFramebufferCreateInfo(*renderPass, DE_LENGTH_OF_ARRAY(attachments), attachments, renderSize.x(), renderSize.y());
const Move<VkFramebuffer> framebuffer = createFramebuffer(vk, device, &framebufferCreateInfo);
vk::beginCommandBuffer(vk, *cmdBuffer, 0u);
{
beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, colorClearColor, depthClearDepth, depthClearStencil);
{
vk.cmdBindVertexBuffers(*cmdBuffer, 0, 1, &vertexBuffer->get(), &vertexBufferOffset);
vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *rootPipeline);
vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayoutSame, 0u, 2u, vecDescriptorSetBoth, 0u, DE_NULL);
vk.cmdDraw(*cmdBuffer, vertexCount, 1u, 0u, 0u);
}
endRenderPass(vk, *cmdBuffer);
const tcu::IVec2 size = { (deInt32)renderSize.x(), (deInt32)renderSize.y() };
copyImageToBuffer(vk, *cmdBuffer, *colorImage, *colorBuffer, size);
copyImageToBuffer(vk, *cmdBuffer, *depthImage, *depthBuffer, size, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, 1u, VK_IMAGE_ASPECT_DEPTH_BIT, VK_IMAGE_ASPECT_DEPTH_BIT);
}
vk::endCommandBuffer(vk, *cmdBuffer);
vk::submitCommandsAndWait(vk, device, m_context.getUniversalQueue(), cmdBuffer.get());
vk::invalidateAlloc(vk, device, colorBuffer.getAllocation());
vk::invalidateAlloc(vk, device, depthBuffer.getAllocation());
const tcu::ConstPixelBufferAccess colorPixelAccess(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1, colorBuffer.getAllocation().getHostPtr());
const tcu::ConstPixelBufferAccess depthPixelAccess(mapVkFormat(depthFormat), renderSize.x(), renderSize.y(), 1, depthBuffer.getAllocation().getHostPtr());
if (!verifyColorImage(colorPixelAccess))
{
log << tcu::TestLog::Image("color", "Rendered image", colorPixelAccess);
return false;
}
if (!verifyDepthImage(depthPixelAccess))
{
log << tcu::TestLog::Image("depth", "Rendered image", depthPixelAccess);
return false;
}
}
return true;
}
bool PipelineLibraryTestInstance::verifyColorImage (const ConstPixelBufferAccess& pba)
{
tcu::TestLog& log = m_context.getTestContext().getLog();
TextureLevel referenceImage (pba.getFormat(), pba.getWidth(), pba.getHeight());
PixelBufferAccess reference (referenceImage);
const int horzEdge = 3 * reference.getWidth() / 4;
const int vertEdge = reference.getHeight() / 2;
const UVec4 green = ivec2uvec(RGBA::green().toIVec());
const UVec4 blue = ivec2uvec(RGBA::blue().toIVec());
const UVec4 black = ivec2uvec(RGBA::black().toIVec());
for (int y = 0; y < reference.getHeight(); ++y)
{
for (int x = 0; x < reference.getWidth(); ++x)
{
if (x < horzEdge)
{
if (y < vertEdge)
reference.setPixel(green, x, y);
else
reference.setPixel(blue, x, y);
}
else
reference.setPixel(black, x, y);
}
}
return intThresholdCompare(log, "colorImage", "colorImage", reference, pba, UVec4(), COMPARE_LOG_RESULT);
}
bool PipelineLibraryTestInstance::verifyDepthImage (const ConstPixelBufferAccess& pba)
{
tcu::TestLog& log = m_context.getTestContext().getLog();
const VkFormat compareFormat = VK_FORMAT_R8_UNORM;
TextureLevel referenceImage (mapVkFormat(compareFormat), pba.getWidth(), pba.getHeight());
PixelBufferAccess reference (referenceImage);
TextureLevel resultImage (mapVkFormat(compareFormat), pba.getWidth(), pba.getHeight());
PixelBufferAccess result (resultImage);
const int horzEdge = 3 * reference.getWidth() / 4;
const int diagonalEdge = (reference.getWidth() + reference.getHeight()) / 2 - 1;
const UVec4 red100 = ivec2uvec(RGBA::red().toIVec());
const UVec4 red025 = UVec4(red100[0] / 4, red100[1] / 4, red100[2] / 4, red100[3]);
const UVec4 red075 = UVec4(3 * red100[0] / 4, 3 * red100[1] / 4, 3 * red100[2] / 4, red100[3]);
for (int y = 0; y < result.getHeight(); ++y)
for (int x = 0; x < result.getWidth(); ++x)
{
const UVec4 pix(static_cast<deUint32>(static_cast<float>(red100[0]) * pba.getPixDepth(x, y)), 0, 0, 0);
result.setPixel(pix, x, y);
}
for (int y = 0; y < reference.getHeight(); ++y)
{
for (int x = 0; x < reference.getWidth(); ++x)
{
if (x < horzEdge)
{
if (x + y < diagonalEdge)
reference.setPixel(red025, x, y);
else
reference.setPixel(red075, x, y);
}
else
reference.setPixel(red100, x, y);
}
}
return intThresholdCompare(log, "depthImage", "depthImage", reference, result, UVec4(), COMPARE_LOG_RESULT);
}
tcu::TestStatus PipelineLibraryTestInstance::iterate (void)
{
VkGraphicsPipelineLibraryFlagBitsEXT graphicsPipelineLibraryFlags[] =
{
VK_GRAPHICS_PIPELINE_LIBRARY_VERTEX_INPUT_INTERFACE_BIT_EXT,
VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT,
VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT,
VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT,
};
const auto graphicsPipelineLibraryFlagsBegin = graphicsPipelineLibraryFlags;
const auto graphicsPipelineLibraryFlagsEnd = graphicsPipelineLibraryFlags + DE_LENGTH_OF_ARRAY(graphicsPipelineLibraryFlags);
deUint32 permutationId = 0;
std::set<deUint32> was;
bool result = true;
do
{
RuntimePipelineTreeConfiguration runtimePipelineTreeConfiguration (m_data.pipelineTreeConfiguration.size());
size_t subsetNdxStart = 0;
deUint32 uniqueTreeSubsetCode = 0;
for (size_t nodeNdx = 0; nodeNdx < runtimePipelineTreeConfiguration.size(); ++nodeNdx)
{
const deUint32 shaderCount = m_data.pipelineTreeConfiguration[nodeNdx].shaderCount;
RuntimePipelineTreeNode& node = runtimePipelineTreeConfiguration[nodeNdx];
node.parentIndex = m_data.pipelineTreeConfiguration[nodeNdx].parentIndex;
node.graphicsPipelineLibraryFlags = 0u;
for (size_t subsetNdx = 0; subsetNdx < shaderCount; ++subsetNdx)
node.graphicsPipelineLibraryFlags |= static_cast<VkGraphicsPipelineLibraryFlagsEXT>(graphicsPipelineLibraryFlags[subsetNdxStart + subsetNdx]);
if (node.parentIndex > 0)
runtimePipelineTreeConfiguration[node.parentIndex].subtreeGraphicsPipelineLibraryFlags |= node.graphicsPipelineLibraryFlags;
// Each shader subset should be tested in each node of tree
subsetNdxStart += shaderCount;
uniqueTreeSubsetCode = (uniqueTreeSubsetCode << 4) | node.graphicsPipelineLibraryFlags;
}
// Check whether this configuration has been tried
if (was.find(uniqueTreeSubsetCode) == was.end())
was.insert(uniqueTreeSubsetCode);
else
continue;
result = result && runTest(runtimePipelineTreeConfiguration, m_data.optimize, m_data.delayedShaderCreate);
if (!result)
{
tcu::TestLog& log = m_context.getTestContext().getLog();
std::ostringstream ess;
for (size_t nodeNdx = 0; nodeNdx < runtimePipelineTreeConfiguration.size(); ++nodeNdx)
{
const RuntimePipelineTreeNode& node = runtimePipelineTreeConfiguration[nodeNdx];
ess << node.parentIndex << " {";
for (size_t subsetNdx = 0; subsetNdx < DE_LENGTH_OF_ARRAY(graphicsPipelineLibraryFlags); ++subsetNdx)
{
if ((node.graphicsPipelineLibraryFlags & graphicsPipelineLibraryFlags[subsetNdx]) == 0)
continue;
ess << getGraphicsPipelineLibraryFlagsString(graphicsPipelineLibraryFlags[subsetNdx]) << " ";
}
ess << "}" << std::endl;
}
log << tcu::TestLog::Message << ess.str() << tcu::TestLog::EndMessage;
return tcu::TestStatus::fail("At permutation " + de::toString(permutationId));
}
++permutationId;
} while (std::next_permutation(graphicsPipelineLibraryFlagsBegin, graphicsPipelineLibraryFlagsEnd));
return tcu::TestStatus::pass("OK");
}
class PipelineLibraryTestCase : public TestCase
{
public:
PipelineLibraryTestCase (tcu::TestContext& context, const char* name, const char* desc, const TestParams data);
~PipelineLibraryTestCase (void);
virtual void checkSupport (Context& context) const;
virtual void initPrograms (SourceCollections& programCollection) const;
virtual TestInstance* createInstance (Context& context) const;
private:
TestParams m_data;
};
PipelineLibraryTestCase::PipelineLibraryTestCase (tcu::TestContext& context, const char* name, const char* desc, const TestParams data)
: vkt::TestCase (context, name, desc)
, m_data (data)
{
}
PipelineLibraryTestCase::~PipelineLibraryTestCase (void)
{
}
void PipelineLibraryTestCase::checkSupport (Context& context) const
{
if (m_data.delayedShaderCreate || (m_data.pipelineTreeConfiguration.size() > 1))
{
context.requireDeviceFunctionality("VK_EXT_graphics_pipeline_library");
const VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT& graphicsPipelineLibraryFeaturesEXT = context.getGraphicsPipelineLibraryFeaturesEXT();
if (!graphicsPipelineLibraryFeaturesEXT.graphicsPipelineLibrary)
TCU_THROW(NotSupportedError, "graphicsPipelineLibraryFeaturesEXT.graphicsPipelineLibrary required");
}
}
void PipelineLibraryTestCase::initPrograms (SourceCollections& programCollection) const
{
std::string vert =
"#version 450\n"
"layout(location = 0) in vec4 in_position;\n"
"layout(set = 0, binding = 0) uniform buf\n"
"{\n"
" vec4 z_coord;\n"
"};\n"
"\n"
"out gl_PerVertex\n"
"{\n"
" vec4 gl_Position;\n"
"};\n"
"\n"
"void main()\n"
"{\n"
" const float z = gl_VertexIndex < 3 ? z_coord.x : z_coord.y;\n"
" gl_Position = vec4(in_position.x, in_position.y, z, 1.0f);\n"
"}\n";
programCollection.glslSources.add("vert") << glu::VertexSource(vert);
std::string frag =
"#version 450\n"
"layout(location = 0) out highp vec4 o_color;\n"
"layout(set = 1, binding = 1) uniform buf\n"
"{\n"
" vec4 colorTop;\n"
" vec4 colorBot;\n"
"};\n"
"\n"
"void main()\n"
"{\n"
" const int middle = " + de::toString(RENDER_SIZE_HEIGHT / 2) + ";\n"
" o_color = int(gl_FragCoord.y - 0.5f) < middle ? colorTop : colorBot;\n"
"}\n";
programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
}
TestInstance* PipelineLibraryTestCase::createInstance (Context& context) const
{
return new PipelineLibraryTestInstance(context, m_data);
}
enum class MiscTestMode
{
INDEPENDENT_PIPELINE_LAYOUT_SETS_FAST_LINKED = 0,
INDEPENDENT_PIPELINE_LAYOUT_SETS_WITH_LINK_TIME_OPTIMIZATION_UNION_HANDLE,
BIND_NULL_DESCRIPTOR_SET,
BIND_NULL_DESCRIPTOR_SET_IN_MONOLITHIC_PIPELINE,
COMPARE_LINK_TIMES
};
struct MiscTestParams
{
MiscTestMode mode;
// attributes used in BIND_NULL_DESCRIPTOR_SET mode
deUint32 layoutsCount;
deUint32 layoutsBits;
};
class PipelineLibraryMiscTestInstance : public TestInstance
{
public:
PipelineLibraryMiscTestInstance (Context& context,
const MiscTestParams& params);
~PipelineLibraryMiscTestInstance (void) = default;
tcu::TestStatus iterate (void);
protected:
tcu::TestStatus runNullDescriptorSet (void);
tcu::TestStatus runNullDescriptorSetInMonolithicPipeline(void);
tcu::TestStatus runIndependentPipelineLayoutSets (bool useLinkTimeOptimization = false);
tcu::TestStatus runCompareLinkTimes (void);
struct VerificationData
{
const tcu::IVec2 point;
const tcu::IVec4 color;
};
tcu::TestStatus verifyResult (const std::vector<VerificationData>& verificationData,
const tcu::ConstPixelBufferAccess& colorPixelAccess) const;
private:
MiscTestParams m_testParams;
const VkFormat m_colorFormat;
const Vec4 m_colorClearColor;
const VkRect2D m_renderArea;
de::MovePtr<ImageWithMemory> m_colorImage;
Move<VkImageView> m_colorImageView;
Move<VkRenderPass> m_renderPass;
Move<VkFramebuffer> m_framebuffer;
Move<VkCommandPool> m_cmdPool;
Move<VkCommandBuffer> m_cmdBuffer;
};
PipelineLibraryMiscTestInstance::PipelineLibraryMiscTestInstance(Context& context, const MiscTestParams& params)
: vkt::TestInstance (context)
, m_testParams (params)
, m_colorFormat (VK_FORMAT_R8G8B8A8_UNORM)
, m_colorClearColor (0.0f, 0.0f, 0.0f, 1.0f)
, m_renderArea (makeRect2D(RENDER_SIZE_WIDTH, RENDER_SIZE_HEIGHT))
{
}
tcu::TestStatus PipelineLibraryMiscTestInstance::iterate (void)
{
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice device = m_context.getDevice();
Allocator& allocator = m_context.getDefaultAllocator();
// create image and image view that will hold rendered frame
const VkImageCreateInfo colorImageCreateInfo = makeColorImageCreateInfo(m_colorFormat, m_renderArea.extent.width, m_renderArea.extent.height);
m_colorImage = de::MovePtr<ImageWithMemory>(new ImageWithMemory(vk, device, allocator, colorImageCreateInfo, MemoryRequirement::Any));
const VkImageViewCreateInfo colorImageViewCreateInfo = makeImageViewCreateInfo(**m_colorImage, m_colorFormat, static_cast<VkImageAspectFlags>(VK_IMAGE_ASPECT_COLOR_BIT));
const Move<VkImageView> colorImageView = createImageView(vk, device, &colorImageViewCreateInfo);
// create renderpass and framebuffer
m_renderPass = makeRenderPass(vk, device, m_colorFormat);
const VkFramebufferCreateInfo framebufferCreateInfo = makeFramebufferCreateInfo(*m_renderPass, 1u, &*colorImageView, m_renderArea.extent.width, m_renderArea.extent.height);
m_framebuffer = createFramebuffer(vk, device, &framebufferCreateInfo);
// create command pool and command buffer
const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
m_cmdPool = createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex);
m_cmdBuffer = allocateCommandBuffer(vk, device, *m_cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
// run selected test
if (m_testParams.mode == MiscTestMode::BIND_NULL_DESCRIPTOR_SET)
return runNullDescriptorSet();
else if (m_testParams.mode == MiscTestMode::BIND_NULL_DESCRIPTOR_SET_IN_MONOLITHIC_PIPELINE)
return runNullDescriptorSetInMonolithicPipeline();
else if (m_testParams.mode == MiscTestMode::INDEPENDENT_PIPELINE_LAYOUT_SETS_FAST_LINKED)
return runIndependentPipelineLayoutSets();
else if (m_testParams.mode == MiscTestMode::INDEPENDENT_PIPELINE_LAYOUT_SETS_WITH_LINK_TIME_OPTIMIZATION_UNION_HANDLE)
return runIndependentPipelineLayoutSets(true);
else if (m_testParams.mode == MiscTestMode::COMPARE_LINK_TIMES)
return runCompareLinkTimes();
DE_ASSERT(DE_FALSE);
return tcu::TestStatus::fail("Fail");
}
tcu::TestStatus PipelineLibraryMiscTestInstance::runNullDescriptorSet(void)
{
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice device = m_context.getDevice();
Allocator& allocator = m_context.getDefaultAllocator();
const VkDeviceSize colorBufferDataSize = static_cast<VkDeviceSize>(m_renderArea.extent.width * m_renderArea.extent.height * tcu::getPixelSize(mapVkFormat(m_colorFormat)));
const VkBufferCreateInfo colorBufferCreateInfo = makeBufferCreateInfo(colorBufferDataSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT);
const BufferWithMemory colorBuffer (vk, device, allocator, colorBufferCreateInfo, MemoryRequirement::HostVisible);
VkDeviceSize uniformBufferDataSize = sizeof(tcu::Vec4);
const VkBufferCreateInfo uniformBufferCreateInfo = makeBufferCreateInfo(uniformBufferDataSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT);
de::MovePtr<BufferWithMemory> uniformBuffer[2];
// setup data in uniform buffers that will give us expected result for validation
const tcu::Vec4 uniformBuffData[]
{
{ -1.00f, 1.00f, 2.0f, -2.00f },
{ 0.00f, 0.20f, 0.6f, 0.75f },
};
for (deUint32 i = 0; i < 2; ++i)
{
uniformBuffer[i] = de::MovePtr<BufferWithMemory>(new BufferWithMemory(vk, device, allocator, uniformBufferCreateInfo, MemoryRequirement::HostVisible));
deMemcpy(uniformBuffer[i]->getAllocation().getHostPtr(), uniformBuffData[i].getPtr(), (size_t)uniformBufferDataSize);
flushAlloc(vk, device, uniformBuffer[i]->getAllocation());
}
const deUint32 maxBitsCount = 8 * sizeof(m_testParams.layoutsBits);
VkDescriptorSetLayout vertDescriptorSetLayouts[maxBitsCount];
VkDescriptorSetLayout fragDescriptorSetLayouts[maxBitsCount];
VkDescriptorSetLayout allDescriptorSetLayouts[maxBitsCount];
// set all layouts to NULL
deMemset(&vertDescriptorSetLayouts, DE_NULL, maxBitsCount * sizeof(VkDescriptorSetLayout));
deMemset(&fragDescriptorSetLayouts, DE_NULL, maxBitsCount * sizeof(VkDescriptorSetLayout));
// create used descriptor set layouts
Move<VkDescriptorSetLayout> usedDescriptorSetLayouts[]
{
DescriptorSetLayoutBuilder()
.addSingleBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT)
.build(vk, device),
DescriptorSetLayoutBuilder()
.addSingleBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_FRAGMENT_BIT)
.build(vk, device)
};
// create descriptor set layouts that are not used by shaders in test - finalPipelineLayout,
// needs to always be the complete pipeline layout with no holes; we can put NULLs in
// DescriptorSetLayouts used by partial pipelines (vertDescriptorSetLayouts and fragDescriptorSetLayouts)
Move<VkDescriptorSetLayout> unusedDescriptorSetLayouts[maxBitsCount];
for (deUint32 i = 0u; i < m_testParams.layoutsCount; ++i)
{
unusedDescriptorSetLayouts[i] = DescriptorSetLayoutBuilder().build(vk, device);
// by default allDescriptorSetLayouts is filled with unused layouts but later
// if test requires this proper indexes are replaced with used layouts
allDescriptorSetLayouts[i] = *unusedDescriptorSetLayouts[i];
}
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = initVulkanStructure();
pipelineLayoutCreateInfo.flags = VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT;
// find set bits
std::vector<deUint32> bitsThatAreSet;
for (deUint32 i = 0u; i < m_testParams.layoutsCount; ++i)
{
if (m_testParams.layoutsBits & (1 << (maxBitsCount - 1 - i)))
bitsThatAreSet.push_back(i);
}
deUint32 usedDescriptorSets = static_cast<deUint32>(bitsThatAreSet.size());
DE_ASSERT(usedDescriptorSets && (usedDescriptorSets < 3u));
deUint32 vertSetIndex = bitsThatAreSet[0];
deUint32 fragSetIndex = 0u;
vertDescriptorSetLayouts[vertSetIndex] = *usedDescriptorSetLayouts[0];
allDescriptorSetLayouts[vertSetIndex] = *usedDescriptorSetLayouts[0];
pipelineLayoutCreateInfo.setLayoutCount = vertSetIndex + 1u;
pipelineLayoutCreateInfo.pSetLayouts = vertDescriptorSetLayouts;
Move<VkPipelineLayout> vertPipelineLayout = createPipelineLayout(vk, device, &pipelineLayoutCreateInfo);
Move<VkPipelineLayout> fragPipelineLayout;
if (usedDescriptorSets == 2u)
{
fragSetIndex = bitsThatAreSet[1];
fragDescriptorSetLayouts[fragSetIndex] = *usedDescriptorSetLayouts[1];
allDescriptorSetLayouts[fragSetIndex] = *usedDescriptorSetLayouts[1];
pipelineLayoutCreateInfo.setLayoutCount = fragSetIndex + 1u;
pipelineLayoutCreateInfo.pSetLayouts = fragDescriptorSetLayouts;
fragPipelineLayout = createPipelineLayout(vk, device, &pipelineLayoutCreateInfo);
}
// create descriptor pool
Move<VkDescriptorPool> descriptorPool = DescriptorPoolBuilder()
.addType(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, usedDescriptorSets)
.build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, usedDescriptorSets);
const VkDescriptorBufferInfo vertShaderBufferInfo = makeDescriptorBufferInfo(**uniformBuffer[0], 0u, uniformBufferDataSize);
Move<VkDescriptorSet> vertDescriptorSet = makeDescriptorSet(vk, device, *descriptorPool, *usedDescriptorSetLayouts[0]);
Move<VkDescriptorSet> fragDescriptorSet;
if (usedDescriptorSets == 1u)
{
// update single descriptors with actual buffer
DescriptorSetUpdateBuilder()
.writeSingle(*vertDescriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &vertShaderBufferInfo)
.update(vk, device);
}
else
{
const VkDescriptorBufferInfo fragShaderBufferInfo = makeDescriptorBufferInfo(**uniformBuffer[1], 0u, uniformBufferDataSize);
fragDescriptorSet = makeDescriptorSet(vk, device, *descriptorPool, *usedDescriptorSetLayouts[1]);
// update both descriptors with actual buffers
DescriptorSetUpdateBuilder()
.writeSingle(*vertDescriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &vertShaderBufferInfo)
.writeSingle(*fragDescriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &fragShaderBufferInfo)
.update(vk, device);
}
pipelineLayoutCreateInfo.setLayoutCount = m_testParams.layoutsCount;
pipelineLayoutCreateInfo.pSetLayouts = allDescriptorSetLayouts;
Move<VkPipelineLayout> finalPipelineLayout = createPipelineLayout(vk, device, &pipelineLayoutCreateInfo);
const deUint32 commonPipelinePartFlags = deUint32(VK_PIPELINE_CREATE_LIBRARY_BIT_KHR);
GraphicsPipelineCreateInfo partialPipelineCreateInfo[]
{
{ *vertPipelineLayout, *m_renderPass, 0, commonPipelinePartFlags },
{ *fragPipelineLayout, *m_renderPass, 0, commonPipelinePartFlags },
};
// fill proper portion of pipeline state
updateVertexInputInterface(m_context, partialPipelineCreateInfo[0], VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP);
updatePreRasterization(m_context, partialPipelineCreateInfo[0], false);
updatePostRasterization(m_context, partialPipelineCreateInfo[1], false);
updateFragmentOutputInterface(m_context, partialPipelineCreateInfo[1]);
Move<VkPipeline> vertPipelinePart;
Move<VkPipeline> fragPipelinePart;
// extend pNext chain and create partial pipelines
{
VkGraphicsPipelineLibraryCreateInfoEXT libraryCreateInfo = makeGraphicsPipelineLibraryCreateInfo(VK_GRAPHICS_PIPELINE_LIBRARY_VERTEX_INPUT_INTERFACE_BIT_EXT | VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT);
appendStructurePtrToVulkanChain(&partialPipelineCreateInfo[0].pNext, &libraryCreateInfo);
vertPipelinePart = createGraphicsPipeline(vk, device, DE_NULL, &partialPipelineCreateInfo[0]);
libraryCreateInfo.flags = VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT | VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT,
appendStructurePtrToVulkanChain(&partialPipelineCreateInfo[1].pNext, &libraryCreateInfo);
fragPipelinePart = createGraphicsPipeline(vk, device, DE_NULL, &partialPipelineCreateInfo[1]);
}
// create final pipeline out of two parts
std::vector<VkPipeline> rawParts = { *vertPipelinePart, *fragPipelinePart };
VkPipelineLibraryCreateInfoKHR linkingInfo = makePipelineLibraryCreateInfo(rawParts);
VkGraphicsPipelineCreateInfo finalPipelineInfo = initVulkanStructure();
finalPipelineInfo.layout = *finalPipelineLayout;
appendStructurePtrToVulkanChain(&finalPipelineInfo.pNext, &linkingInfo);
Move<VkPipeline> pipeline = createGraphicsPipeline(vk, device, DE_NULL, &finalPipelineInfo);
vk::beginCommandBuffer(vk, *m_cmdBuffer, 0u);
{
// change color image layout
const VkImageMemoryBarrier initialImageBarrier = makeImageMemoryBarrier(
0, // VkAccessFlags srcAccessMask;
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // VkAccessFlags dstAccessMask;
VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout oldLayout;
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // VkImageLayout newLayout;
**m_colorImage, // VkImage image;
{ VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u } // VkImageSubresourceRange subresourceRange;
);
vk.cmdPipelineBarrier(*m_cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, (VkDependencyFlags)0, 0, DE_NULL, 0, DE_NULL, 1, &initialImageBarrier);
// wait for uniform buffers
std::vector<VkBufferMemoryBarrier> initialBufferBarriers(2u, makeBufferMemoryBarrier(
VK_ACCESS_HOST_WRITE_BIT, // VkAccessFlags2KHR srcAccessMask
VK_ACCESS_TRANSFER_READ_BIT, // VkAccessFlags2KHR dstAccessMask
uniformBuffer[0]->get(), // VkBuffer buffer
0u, // VkDeviceSize offset
uniformBufferDataSize // VkDeviceSize size
));
initialBufferBarriers[1].buffer = uniformBuffer[1]->get();
vk.cmdPipelineBarrier(*m_cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, (VkDependencyFlags)0, 0, DE_NULL, 2, initialBufferBarriers.data(), 0, DE_NULL);
beginRenderPass(vk, *m_cmdBuffer, *m_renderPass, *m_framebuffer, m_renderArea, m_colorClearColor);
vk.cmdBindPipeline(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
vk.cmdBindDescriptorSets(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *finalPipelineLayout, vertSetIndex, 1u, &*vertDescriptorSet, 0u, DE_NULL);
if (usedDescriptorSets == 2u)
vk.cmdBindDescriptorSets(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *finalPipelineLayout, fragSetIndex, 1u, &*fragDescriptorSet, 0u, DE_NULL);
vk.cmdDraw(*m_cmdBuffer, 4, 1u, 0u, 0u);
endRenderPass(vk, *m_cmdBuffer);
const tcu::IVec2 size { (deInt32)m_renderArea.extent.width, (deInt32)m_renderArea.extent.height };
copyImageToBuffer(vk, *m_cmdBuffer, **m_colorImage, *colorBuffer, size);
}
vk::endCommandBuffer(vk, *m_cmdBuffer);
vk::submitCommandsAndWait(vk, device, m_context.getUniversalQueue(), *m_cmdBuffer);
vk::invalidateAlloc(vk, device, colorBuffer.getAllocation());
const tcu::ConstPixelBufferAccess colorPixelAccess(mapVkFormat(m_colorFormat), m_renderArea.extent.width, m_renderArea.extent.height, 1, colorBuffer.getAllocation().getHostPtr());
// verify result
deInt32 width = (deInt32)m_renderArea.extent.width;
deInt32 height = (deInt32)m_renderArea.extent.height;
const std::vector<VerificationData> verificationData
{
{ { 1, 1 }, { 0, 51, 153, 191 } }, // note COLOR_COMPONENTS_NO_RED is used
{ { width / 2, height / 2 }, { 0, 51, 153, 191 } },
{ { width - 2, height - 2 }, { 0, 0, 0, 255 } } // clear color
};
return verifyResult(verificationData, colorPixelAccess);
}
tcu::TestStatus PipelineLibraryMiscTestInstance::runNullDescriptorSetInMonolithicPipeline()
{
// VK_NULL_HANDLE can be used for descriptor set layouts when creating a pipeline layout whether independent sets are used or not,
// as long as graphics pipeline libraries are enabled; VK_NULL_HANDLE is also alowed for a descriptor set under the same conditions
// when using vkCmdBindDescriptorSets
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice device = m_context.getDevice();
Allocator& allocator = m_context.getDefaultAllocator();
const VkDeviceSize colorBufferDataSize = static_cast<VkDeviceSize>(m_renderArea.extent.width * m_renderArea.extent.height * tcu::getPixelSize(mapVkFormat(m_colorFormat)));
const VkBufferCreateInfo colorBufferCreateInfo = makeBufferCreateInfo(colorBufferDataSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT);
const BufferWithMemory colorBuffer(vk, device, allocator, colorBufferCreateInfo, MemoryRequirement::HostVisible);
const tcu::Vec4 uniformBuffData { 0.0f, 0.20f, 0.6f, 0.75f };
VkDeviceSize uniformBufferDataSize = sizeof(tcu::Vec4);
const VkBufferCreateInfo uniformBufferCreateInfo = makeBufferCreateInfo(uniformBufferDataSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT);
de::MovePtr<BufferWithMemory> uniformBuffer = de::MovePtr<BufferWithMemory>(new BufferWithMemory(vk, device, allocator, uniformBufferCreateInfo, MemoryRequirement::HostVisible));
deMemcpy(uniformBuffer->getAllocation().getHostPtr(), uniformBuffData.getPtr(), (size_t)uniformBufferDataSize);
flushAlloc(vk, device, uniformBuffer->getAllocation());
// create descriptor set layouts - first unused, second used
Move<VkDescriptorSetLayout> descriptorSetLayout
{
DescriptorSetLayoutBuilder()
.addSingleBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_FRAGMENT_BIT)
.build(vk, device)
};
Move<VkDescriptorPool> allDescriptorPool = DescriptorPoolBuilder()
.addType(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1)
.build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1);
// create descriptor set
Move<VkDescriptorSet> descriptorSet = makeDescriptorSet(vk, device, *allDescriptorPool, *descriptorSetLayout);
// update descriptor with actual buffer
const VkDescriptorBufferInfo shaderBufferInfo = makeDescriptorBufferInfo(**uniformBuffer, 0u, uniformBufferDataSize);
DescriptorSetUpdateBuilder()
.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &shaderBufferInfo)
.update(vk, device);
// create a pipeline layout with its first descriptor set layout as VK_NULL_HANDLE
// and a second with a valid descriptor set layout containing a buffer
VkDescriptorSet rawDescriptorSets[] = { DE_NULL, *descriptorSet };
VkDescriptorSetLayout rawDescriptorSetLayouts[] = { DE_NULL, *descriptorSetLayout };
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = initVulkanStructure();
pipelineLayoutCreateInfo.setLayoutCount = 2u;
pipelineLayoutCreateInfo.pSetLayouts = rawDescriptorSetLayouts;
Move<VkPipelineLayout> pipelineLayout = createPipelineLayout(vk, device, &pipelineLayoutCreateInfo);
// create monolithic graphics pipeline
GraphicsPipelineCreateInfo pipelineCreateInfo(*pipelineLayout, *m_renderPass, 0, 0u);
updateVertexInputInterface(m_context, pipelineCreateInfo, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, 0u);
updatePreRasterization(m_context, pipelineCreateInfo, false);
updatePostRasterization(m_context, pipelineCreateInfo, false);
updateFragmentOutputInterface(m_context, pipelineCreateInfo);
Move<VkPipeline> pipeline = createGraphicsPipeline(vk, device, DE_NULL, &pipelineCreateInfo);
vk::beginCommandBuffer(vk, *m_cmdBuffer, 0u);
{
// change color image layout
const VkImageMemoryBarrier initialImageBarrier = makeImageMemoryBarrier(
0, // VkAccessFlags srcAccessMask;
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // VkAccessFlags dstAccessMask;
VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout oldLayout;
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // VkImageLayout newLayout;
**m_colorImage, // VkImage image;
{ VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u } // VkImageSubresourceRange subresourceRange;
);
vk.cmdPipelineBarrier(*m_cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, (VkDependencyFlags)0, 0, DE_NULL, 0, DE_NULL, 1, &initialImageBarrier);
// wait for uniform buffer
const VkBufferMemoryBarrier initialBufferBarrier = makeBufferMemoryBarrier(
VK_ACCESS_HOST_WRITE_BIT, // VkAccessFlags2KHR srcAccessMask
VK_ACCESS_UNIFORM_READ_BIT, // VkAccessFlags2KHR dstAccessMask
uniformBuffer->get(), // VkBuffer buffer
0u, // VkDeviceSize offset
uniformBufferDataSize // VkDeviceSize size
);
vk.cmdPipelineBarrier(*m_cmdBuffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, (VkDependencyFlags)0, 0, DE_NULL, 1, &initialBufferBarrier, 0, DE_NULL);
beginRenderPass(vk, *m_cmdBuffer, *m_renderPass, *m_framebuffer, m_renderArea, m_colorClearColor);
vk.cmdBindPipeline(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
vk.cmdBindDescriptorSets(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0u, 2u, rawDescriptorSets, 0u, DE_NULL);
vk.cmdDraw(*m_cmdBuffer, 4, 1u, 0u, 0u);
endRenderPass(vk, *m_cmdBuffer);
const tcu::IVec2 size{ (deInt32)m_renderArea.extent.width, (deInt32)m_renderArea.extent.height };
copyImageToBuffer(vk, *m_cmdBuffer, **m_colorImage, *colorBuffer, size);
}
vk::endCommandBuffer(vk, *m_cmdBuffer);
vk::submitCommandsAndWait(vk, device, m_context.getUniversalQueue(), *m_cmdBuffer);
vk::invalidateAlloc(vk, device, colorBuffer.getAllocation());
const tcu::ConstPixelBufferAccess colorPixelAccess(mapVkFormat(m_colorFormat), m_renderArea.extent.width, m_renderArea.extent.height, 1, colorBuffer.getAllocation().getHostPtr());
// verify result
deInt32 width = (deInt32)m_renderArea.extent.width;
deInt32 height = (deInt32)m_renderArea.extent.height;
tcu::IVec4 outColor
{
0, // r is 0 because COLOR_COMPONENTS_NO_RED is used
static_cast<int>(uniformBuffData[1] * 255),
static_cast<int>(uniformBuffData[2] * 255),
static_cast<int>(uniformBuffData[3] * 255)
};
const std::vector<VerificationData> verificationData
{
{ { 1, 1 }, outColor },
{ { width / 2, height / 2 }, outColor },
{ { width - 2, height - 2 }, { 0, 0, 0, 255 } } // clear color
};
return verifyResult(verificationData, colorPixelAccess);
}
tcu::TestStatus PipelineLibraryMiscTestInstance::runIndependentPipelineLayoutSets (bool useLinkTimeOptimization)
{
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice device = m_context.getDevice();
Allocator& allocator = m_context.getDefaultAllocator();
const VkDeviceSize colorBufferDataSize = static_cast<VkDeviceSize>(m_renderArea.extent.width * m_renderArea.extent.height * tcu::getPixelSize(mapVkFormat(m_colorFormat)));
const VkBufferCreateInfo colorBufferCreateInfo = makeBufferCreateInfo(colorBufferDataSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT);
const BufferWithMemory colorBuffer (vk, device, allocator, colorBufferCreateInfo, MemoryRequirement::HostVisible);
VkDeviceSize uniformBufferDataSize = sizeof(tcu::Vec4);
const VkBufferCreateInfo uniformBufferCreateInfo = makeBufferCreateInfo(uniformBufferDataSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT);
de::MovePtr<BufferWithMemory> uniformBuffer[3];
// setup data in uniform buffers that will give us expected result for validation
const tcu::Vec4 uniformBuffData[3]
{
{ 4.00f, 3.00f, -1.0f, 4.00f },
{ 0.10f, 0.25f, -0.5f, 0.05f },
{ -5.00f, -2.00f, 3.0f, -6.00f },
};
for (deUint32 i = 0; i < 3; ++i)
{
uniformBuffer[i] = de::MovePtr<BufferWithMemory>(new BufferWithMemory(vk, device, allocator, uniformBufferCreateInfo, MemoryRequirement::HostVisible));
deMemcpy(uniformBuffer[i]->getAllocation().getHostPtr(), uniformBuffData[i].getPtr(), (size_t)uniformBufferDataSize);
flushAlloc(vk, device, uniformBuffer[i]->getAllocation());
}
// create three descriptor set layouts
Move<VkDescriptorSetLayout> descriptorSetLayouts[3];
descriptorSetLayouts[0] = DescriptorSetLayoutBuilder()
.addSingleBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT)
.build(vk, device);
descriptorSetLayouts[1] = DescriptorSetLayoutBuilder()
.addSingleBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_FRAGMENT_BIT)
.build(vk, device);
descriptorSetLayouts[2] = DescriptorSetLayoutBuilder()
.addSingleBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT)
.build(vk, device);
// for the link time opt (and when null handle is used) use total pipeline layout recreated without the INDEPENDENT SETS bit
deUint32 allLayoutsFlag = deUint32(VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT);
if (useLinkTimeOptimization)
allLayoutsFlag = 0u;
// Pre-rasterization stage library has sets 0, 1, 2
// * set 0 has descriptors
// * set 1 has no descriptors
// * set 2 has descriptors
// Fragment stage library has sets 0, 1
// * set 0 has descriptors
// * set 1 has descriptors
VkDescriptorSetLayout vertDescriptorSetLayouts[] = { *descriptorSetLayouts[0], DE_NULL, *descriptorSetLayouts[2] };
VkDescriptorSetLayout fragDescriptorSetLayouts[] = { *descriptorSetLayouts[0], *descriptorSetLayouts[1] };
VkDescriptorSetLayout allDescriptorSetLayouts[] = { *descriptorSetLayouts[0], *descriptorSetLayouts[1], *descriptorSetLayouts[2] };
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = initVulkanStructure();
pipelineLayoutCreateInfo.flags = allLayoutsFlag;
pipelineLayoutCreateInfo.setLayoutCount = 3u;
pipelineLayoutCreateInfo.pSetLayouts = allDescriptorSetLayouts;
Move<VkPipelineLayout> allLayouts = createPipelineLayout(vk, device, &pipelineLayoutCreateInfo);
pipelineLayoutCreateInfo.flags = deUint32(VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT);
pipelineLayoutCreateInfo.pSetLayouts = vertDescriptorSetLayouts;
Move<VkPipelineLayout> vertLayouts = createPipelineLayout(vk, device, &pipelineLayoutCreateInfo);
pipelineLayoutCreateInfo.setLayoutCount = 2u;
pipelineLayoutCreateInfo.pSetLayouts = fragDescriptorSetLayouts;
Move<VkPipelineLayout> fragLayouts = createPipelineLayout(vk, device, &pipelineLayoutCreateInfo);
Move<VkDescriptorPool> allDescriptorPool = DescriptorPoolBuilder()
.addType(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 3)
.build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 3);
// create three descriptor sets
Move<VkDescriptorSet> descriptorSetA = makeDescriptorSet(vk, device, *allDescriptorPool, *descriptorSetLayouts[0]);
Move<VkDescriptorSet> descriptorSetB = makeDescriptorSet(vk, device, *allDescriptorPool, *descriptorSetLayouts[1]);
Move<VkDescriptorSet> descriptorSetC = makeDescriptorSet(vk, device, *allDescriptorPool, *descriptorSetLayouts[2]);
VkDescriptorSet allDescriptorSets[] = { *descriptorSetA , *descriptorSetB , *descriptorSetC };
// update descriptors with actual buffers
const VkDescriptorBufferInfo shaderBufferAInfo = makeDescriptorBufferInfo(**uniformBuffer[0], 0u, uniformBufferDataSize);
const VkDescriptorBufferInfo shaderBufferBInfo = makeDescriptorBufferInfo(**uniformBuffer[1], 0u, uniformBufferDataSize);
const VkDescriptorBufferInfo shaderBufferCInfo = makeDescriptorBufferInfo(**uniformBuffer[2], 0u, uniformBufferDataSize);
DescriptorSetUpdateBuilder()
.writeSingle(*descriptorSetA, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &shaderBufferAInfo)
.writeSingle(*descriptorSetB, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &shaderBufferBInfo)
.writeSingle(*descriptorSetC, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &shaderBufferCInfo)
.update(vk, device);
deUint32 commonPipelinePartFlags = deUint32(VK_PIPELINE_CREATE_LIBRARY_BIT_KHR);
deUint32 finalPipelineFlag = 0u;
if (useLinkTimeOptimization)
{
commonPipelinePartFlags |= deUint32(VK_PIPELINE_CREATE_RETAIN_LINK_TIME_OPTIMIZATION_INFO_BIT_EXT);
finalPipelineFlag = deUint32(VK_PIPELINE_CREATE_LINK_TIME_OPTIMIZATION_BIT_EXT);
}
GraphicsPipelineCreateInfo partialPipelineCreateInfo[]
{
{ DE_NULL, *m_renderPass, 0, commonPipelinePartFlags },
{ *vertLayouts, *m_renderPass, 0, commonPipelinePartFlags },
{ *fragLayouts, *m_renderPass, 0, commonPipelinePartFlags },
{ DE_NULL, *m_renderPass, 0, commonPipelinePartFlags }
};
// fill proper portion of pipeline state
updateVertexInputInterface (m_context, partialPipelineCreateInfo[0], VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, 0u);
updatePreRasterization (m_context, partialPipelineCreateInfo[1], false);
updatePostRasterization (m_context, partialPipelineCreateInfo[2], false);
updateFragmentOutputInterface (m_context, partialPipelineCreateInfo[3]);
// extend pNext chain and create all partial pipelines
std::vector<VkPipeline> rawParts(4u, DE_NULL);
std::vector<Move<VkPipeline> > pipelineParts;
pipelineParts.reserve(4u);
VkGraphicsPipelineLibraryCreateInfoEXT libraryCreateInfo = makeGraphicsPipelineLibraryCreateInfo(VK_GRAPHICS_PIPELINE_LIBRARY_VERTEX_INPUT_INTERFACE_BIT_EXT);
for (deUint32 i = 0 ; i < 4u ; ++i)
{
libraryCreateInfo.flags = GRAPHICS_PIPELINE_LIBRARY_FLAGS[i];
appendStructurePtrToVulkanChain(&partialPipelineCreateInfo[i].pNext, &libraryCreateInfo);
pipelineParts.emplace_back(createGraphicsPipeline(vk, device, DE_NULL, &partialPipelineCreateInfo[i]));
rawParts[i] = *pipelineParts[i];
}
// create final pipeline out of four parts
VkPipelineLibraryCreateInfoKHR linkingInfo = makePipelineLibraryCreateInfo(rawParts);
VkGraphicsPipelineCreateInfo finalPipelineInfo = initVulkanStructure();
finalPipelineInfo.flags = finalPipelineFlag;
finalPipelineInfo.layout = *allLayouts;
appendStructurePtrToVulkanChain(&finalPipelineInfo.pNext, &linkingInfo);
Move<VkPipeline> pipeline = createGraphicsPipeline(vk, device, DE_NULL, &finalPipelineInfo);
vk::beginCommandBuffer(vk, *m_cmdBuffer, 0u);
{
// change color image layout
const VkImageMemoryBarrier initialImageBarrier = makeImageMemoryBarrier(
0, // VkAccessFlags srcAccessMask;
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // VkAccessFlags dstAccessMask;
VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout oldLayout;
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // VkImageLayout newLayout;
**m_colorImage, // VkImage image;
{ VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u } // VkImageSubresourceRange subresourceRange;
);
vk.cmdPipelineBarrier(*m_cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, (VkDependencyFlags)0, 0, DE_NULL, 0, DE_NULL, 1, &initialImageBarrier);
// wait for uniform buffers
std::vector<VkBufferMemoryBarrier> initialBufferBarriers(3u, makeBufferMemoryBarrier(
VK_ACCESS_HOST_WRITE_BIT, // VkAccessFlags2KHR srcAccessMask
VK_ACCESS_UNIFORM_READ_BIT, // VkAccessFlags2KHR dstAccessMask
uniformBuffer[0]->get(), // VkBuffer buffer
0u, // VkDeviceSize offset
uniformBufferDataSize // VkDeviceSize size
));
initialBufferBarriers[1].buffer = uniformBuffer[1]->get();
initialBufferBarriers[2].buffer = uniformBuffer[2]->get();
vk.cmdPipelineBarrier(*m_cmdBuffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, (VkDependencyFlags)0, 0, DE_NULL, 3, initialBufferBarriers.data(), 0, DE_NULL);
beginRenderPass(vk, *m_cmdBuffer, *m_renderPass, *m_framebuffer, m_renderArea, m_colorClearColor);
vk.cmdBindPipeline(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
vk.cmdBindDescriptorSets(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *allLayouts, 0u, 3u, allDescriptorSets, 0u, DE_NULL);
vk.cmdDraw(*m_cmdBuffer, 4, 1u, 0u, 0u);
endRenderPass(vk, *m_cmdBuffer);
const tcu::IVec2 size{ (deInt32)m_renderArea.extent.width, (deInt32)m_renderArea.extent.height };
copyImageToBuffer(vk, *m_cmdBuffer, **m_colorImage, *colorBuffer, size);
}
vk::endCommandBuffer(vk, *m_cmdBuffer);
vk::submitCommandsAndWait(vk, device, m_context.getUniversalQueue(), *m_cmdBuffer);
vk::invalidateAlloc(vk, device, colorBuffer.getAllocation());
const tcu::ConstPixelBufferAccess colorPixelAccess(mapVkFormat(m_colorFormat), m_renderArea.extent.width, m_renderArea.extent.height, 1, colorBuffer.getAllocation().getHostPtr());
// verify result
deInt32 width = (deInt32)m_renderArea.extent.width;
deInt32 height = (deInt32)m_renderArea.extent.height;
const std::vector<VerificationData> verificationData
{
{ { 1, 1 }, { 0, 191, 127, 51 } }, // note COLOR_COMPONENTS_NO_RED is used
{ { width / 2, height / 2 }, { 0, 191, 127, 51 } },
{ { width - 2, height - 2 }, { 0, 0, 0, 255 } } // clear color
};
return verifyResult(verificationData, colorPixelAccess);
}
tcu::TestStatus PipelineLibraryMiscTestInstance::runCompareLinkTimes (void)
{
const deUint32 uniqueLibrariesCount = 2u;
const deUint32 pipelinesCount = 4u * uniqueLibrariesCount;
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice device = m_context.getDevice();
tcu::TestLog& log = m_context.getTestContext().getLog();
bool allChecksPassed = true;
VkPipelineLayoutCreateInfo pipelineLayoutParams = initVulkanStructure();
Move<VkPipelineLayout> layout = createPipelineLayout(vk, device, &pipelineLayoutParams);
GraphicsPipelineCreateInfo partialPipelineCreateInfo[]
{
{ *layout, *m_renderPass, 0, VK_PIPELINE_CREATE_LIBRARY_BIT_KHR },
{ *layout, *m_renderPass, 0, VK_PIPELINE_CREATE_LIBRARY_BIT_KHR },
{ *layout, *m_renderPass, 0, VK_PIPELINE_CREATE_LIBRARY_BIT_KHR },
{ *layout, *m_renderPass, 0, VK_PIPELINE_CREATE_LIBRARY_BIT_KHR },
{ *layout, *m_renderPass, 0, VK_PIPELINE_CREATE_LIBRARY_BIT_KHR },
{ *layout, *m_renderPass, 0, VK_PIPELINE_CREATE_LIBRARY_BIT_KHR },
{ *layout, *m_renderPass, 0, VK_PIPELINE_CREATE_LIBRARY_BIT_KHR },
{ *layout, *m_renderPass, 0, VK_PIPELINE_CREATE_LIBRARY_BIT_KHR },
};
// fill proper portion of pipeline state - this cant be easily done in a scalable loop
updateVertexInputInterface (m_context, partialPipelineCreateInfo[0], VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP);
updateVertexInputInterface (m_context, partialPipelineCreateInfo[1], VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
updatePreRasterization (m_context, partialPipelineCreateInfo[2], false, VK_POLYGON_MODE_FILL);
updatePreRasterization (m_context, partialPipelineCreateInfo[3], false, VK_POLYGON_MODE_LINE);
updatePostRasterization (m_context, partialPipelineCreateInfo[4], false, true);
updatePostRasterization (m_context, partialPipelineCreateInfo[5], false, false);
updateFragmentOutputInterface (m_context, partialPipelineCreateInfo[6], 0xf);
updateFragmentOutputInterface (m_context, partialPipelineCreateInfo[7]);
// construct all pipeline parts and mesure time it took
struct PipelinePartData
{
Move<VkPipeline> pipelineHandle;
std::chrono::duration<deInt64, std::nano> creationDuration;
};
std::vector<PipelinePartData> pipelinePartData(pipelinesCount);
VkGraphicsPipelineLibraryCreateInfoEXT libraryCreateInfo = makeGraphicsPipelineLibraryCreateInfo(VK_GRAPHICS_PIPELINE_LIBRARY_VERTEX_INPUT_INTERFACE_BIT_EXT);
for (deUint32 i = 0; i < pipelinesCount; ++i)
{
appendStructurePtrToVulkanChain(&partialPipelineCreateInfo[i].pNext, &libraryCreateInfo);
libraryCreateInfo.flags = GRAPHICS_PIPELINE_LIBRARY_FLAGS[i / 2];
auto& partData = pipelinePartData[i];
auto timeStart = std::chrono::high_resolution_clock::now();
partData.pipelineHandle = createGraphicsPipeline(vk, device, DE_NULL, partialPipelineCreateInfo + i);
partData.creationDuration = std::chrono::high_resolution_clock::now() - timeStart;
}
// iterate over all combinations of parts
for (deUint32 i = 0u ; i < (deUint32)dePow(4, uniqueLibrariesCount) ; ++i)
{
// select new unique combination of parts
deUint32 vertexInputIndex = (i ) % 2;
deUint32 preRasterizationIndex = (i / 2) % 2;
deUint32 fragmentStateIndex = (i / 4) % 2;
deUint32 fragmentOutputIndex = (i / 8) % 2;
const auto& vertexInputData = pipelinePartData[ vertexInputIndex];
const auto& preRasterizationData = pipelinePartData[ uniqueLibrariesCount + preRasterizationIndex];
const auto& fragmentStateData = pipelinePartData[2 * uniqueLibrariesCount + fragmentStateIndex];
const auto& fragmentOutputData = pipelinePartData[3 * uniqueLibrariesCount + fragmentOutputIndex];
std::vector<VkPipeline> pipelinesToLink
{
*vertexInputData.pipelineHandle,
*preRasterizationData.pipelineHandle,
*fragmentStateData.pipelineHandle,
*fragmentOutputData.pipelineHandle,
};
VkPipelineLibraryCreateInfoKHR linkingInfo = makePipelineLibraryCreateInfo(pipelinesToLink);
VkGraphicsPipelineCreateInfo finalPipelineInfo = initVulkanStructure();
finalPipelineInfo.layout = *layout;
appendStructurePtrToVulkanChain(&finalPipelineInfo.pNext, &linkingInfo);
// link pipeline without the optimised bit, and record the time taken to link it
auto timeStart = std::chrono::high_resolution_clock::now();
Move<VkPipeline> pipeline = createGraphicsPipeline(vk, device, DE_NULL, &finalPipelineInfo);
const auto linkingTime = std::chrono::high_resolution_clock::now() - timeStart;
const auto creationTime = preRasterizationData.creationDuration + fragmentStateData.creationDuration;
if (linkingTime > (10 * creationTime))
{
allChecksPassed = false;
log << tcu::TestLog::Message
<< "Liking time (" << linkingTime.count() << ") of combination " << i
<< " is more then ten times greater than creation of both pre-rasterization and fragment states (" << creationTime.count() << ")"
<< tcu::TestLog::EndMessage;
}
}
if (allChecksPassed)
return tcu::TestStatus::pass("Pass");
return tcu::TestStatus(QP_TEST_RESULT_QUALITY_WARNING, "Liking of one or more combinations took to long");
}
tcu::TestStatus PipelineLibraryMiscTestInstance::verifyResult(const std::vector<VerificationData>& verificationData, const tcu::ConstPixelBufferAccess& colorPixelAccess) const
{
const deInt32 epsilon = 1;
for (const auto& v : verificationData)
{
const IVec4 pixel = colorPixelAccess.getPixelInt(v.point.x(), v.point.y());
const IVec4 diff = pixel - v.color;
for (deUint32 compNdx = 0; compNdx < 4u; ++compNdx)
{
if (de::abs(diff[compNdx]) > epsilon)
{
const Vec4 pixelBias(0.0f);
const Vec4 pixelScale(1.0f);
m_context.getTestContext().getLog()
<< TestLog::Image("Result", "Result", colorPixelAccess, pixelScale, pixelBias)
<< tcu::TestLog::Message
<< "For texel " << v.point << " expected color "
<< v.color << " got: " << pixel
<< tcu::TestLog::EndMessage;
return tcu::TestStatus::fail("Fail");
}
}
}
return tcu::TestStatus::pass("Pass");
}
class PipelineLibraryMiscTestCase : public TestCase
{
public:
PipelineLibraryMiscTestCase (tcu::TestContext& context, const char* name, const MiscTestParams data);
~PipelineLibraryMiscTestCase (void) = default;
void checkSupport (Context& context) const;
void initPrograms (SourceCollections& programCollection) const;
TestInstance* createInstance (Context& context) const;
private:
MiscTestParams m_testParams;
};
PipelineLibraryMiscTestCase::PipelineLibraryMiscTestCase(tcu::TestContext& context, const char* name, const MiscTestParams params)
: TestCase (context, name, "")
, m_testParams (params)
{
}
void PipelineLibraryMiscTestCase::checkSupport(Context& context) const
{
context.requireDeviceFunctionality("VK_EXT_graphics_pipeline_library");
// VK_KHR_pipeline_library must be supported if the VK_EXT_graphics_pipeline_library extension is supported.
// Note that vktTestCase skips enabling VK_KHR_pipeline_library by default and we can't use requireDeviceFunctionality for it.
const auto supportedExtensions = enumerateDeviceExtensionProperties(context.getInstanceInterface(), context.getPhysicalDevice(), DE_NULL);
if (!isExtensionStructSupported(supportedExtensions, RequiredExtension("VK_KHR_pipeline_library")))
TCU_FAIL("VK_KHR_pipeline_library not supported but VK_EXT_graphics_pipeline_library supported");
if ((m_testParams.mode == MiscTestMode::INDEPENDENT_PIPELINE_LAYOUT_SETS_FAST_LINKED) &&
!context.getGraphicsPipelineLibraryPropertiesEXT().graphicsPipelineLibraryFastLinking)
TCU_THROW(NotSupportedError, "graphicsPipelineLibraryFastLinking is not supported");
}
void PipelineLibraryMiscTestCase::initPrograms(SourceCollections& programCollection) const
{
if ((m_testParams.mode == MiscTestMode::BIND_NULL_DESCRIPTOR_SET) ||
(m_testParams.mode == MiscTestMode::BIND_NULL_DESCRIPTOR_SET_IN_MONOLITHIC_PIPELINE))
{
std::string vertDefinition = "";
std::string fragDefinition = "";
std::string vertValue = " vec4 v = vec4(-1.0, 1.0, 2.0, -2.0);\n";
std::string fragValue = " vec4 v = vec4(0.0, 0.2, 0.6, 0.75);\n";
// define lambda that creates proper uniform buffer definition
auto constructBufferDefinition = [](deUint32 setIndex)
{
return std::string("layout(set = ") + std::to_string(setIndex) + ", binding = 0) uniform buf\n"
"{\n"
" vec4 v;\n"
"};\n\n";
};
if (m_testParams.mode == MiscTestMode::BIND_NULL_DESCRIPTOR_SET_IN_MONOLITHIC_PIPELINE)
{
// descriptor set 0 will be DE_NULL, descriptor set 1 will be valid buffer with color
fragDefinition = constructBufferDefinition(1);
fragValue = "";
}
else if (m_testParams.layoutsBits > 0u)
{
std::vector<deUint32> bitsThatAreSet;
const deUint32 maxBitsCount = 8 * sizeof(m_testParams.layoutsBits);
// find set bits
for (deUint32 i = 0u; i < m_testParams.layoutsCount; ++i)
{
if (m_testParams.layoutsBits & (1 << (maxBitsCount - 1 - i)))
bitsThatAreSet.push_back(i);
}
// there should be 1 or 2 bits set
DE_ASSERT((bitsThatAreSet.size() > 0) && (bitsThatAreSet.size() < 3));
vertDefinition = constructBufferDefinition(bitsThatAreSet[0]);
vertValue = "";
if (bitsThatAreSet.size() == 2u)
{
fragDefinition = constructBufferDefinition(bitsThatAreSet[1]);
fragValue = "";
}
}
programCollection.glslSources.add("vert") << glu::VertexSource(
std::string("#version 450\n"
"precision mediump int;\nprecision highp float;\n") +
vertDefinition +
"out gl_PerVertex\n"
"{\n"
" vec4 gl_Position;\n"
"};\n\n"
"void main()\n"
"{\n" +
vertValue +
" const float x = (v.x+v.z*((gl_VertexIndex & 2)>>1));\n"
" const float y = (v.y+v.w* (gl_VertexIndex % 2));\n"
// note: there won't be full screen quad because of used scissors
" gl_Position = vec4(x, y, 0.0, 1.0);\n"
"}\n");
programCollection.glslSources.add("frag") << glu::FragmentSource(
std::string("#version 450\n"
"precision mediump int; precision highp float;"
"layout(location = 0) out highp vec4 o_color;\n") +
fragDefinition +
"void main()\n"
"{\n" +
fragValue +
" o_color = v;\n"
"}\n");
}
else if ((m_testParams.mode == MiscTestMode::INDEPENDENT_PIPELINE_LAYOUT_SETS_FAST_LINKED) ||
(m_testParams.mode == MiscTestMode::INDEPENDENT_PIPELINE_LAYOUT_SETS_WITH_LINK_TIME_OPTIMIZATION_UNION_HANDLE))
{
programCollection.glslSources.add("vert") << glu::VertexSource(
"#version 450\n"
"precision mediump int; precision highp float;"
"layout(location = 0) in vec4 in_position;\n"
"layout(set = 0, binding = 0) uniform bufA\n"
"{\n"
" vec4 valueA;\n"
"};\n"
"layout(set = 2, binding = 0) uniform bufC\n"
"{\n"
" vec4 valueC;\n"
"};\n"
"out gl_PerVertex\n"
"{\n"
" vec4 gl_Position;\n"
"};\n\n"
"void main()\n"
"{\n"
// note: values in buffers were set to get vec4(-1, 1, 2, -2)
" const vec4 v = valueA + valueC;\n"
" const float x = (v.x+v.z*((gl_VertexIndex & 2)>>1));\n"
" const float y = (v.y+v.w* (gl_VertexIndex % 2));\n"
// note: there won't be full screen quad because of used scissors
" gl_Position = vec4(x, y, 0.0, 1.0);\n"
"}\n");
programCollection.glslSources.add("frag") << glu::FragmentSource(
"#version 450\n"
"precision mediump int; precision highp float;"
"layout(location = 0) out highp vec4 o_color;\n"
"layout(set = 0, binding = 0) uniform bufA\n"
"{\n"
" vec4 valueA;\n"
"};\n"
"layout(set = 1, binding = 0) uniform bufB\n"
"{\n"
" vec4 valueB;\n"
"};\n\n"
"void main()\n"
"{\n"
// note: values in buffers were set to get vec4(0.0, 0.75, 0.5, 0.2)
" o_color = valueA * valueB;\n"
"}\n");
}
if (m_testParams.mode == MiscTestMode::COMPARE_LINK_TIMES)
{
programCollection.glslSources.add("vert") << glu::VertexSource(
"#version 450\n"
"precision mediump int; precision highp float;"
"layout(location = 0) in vec4 in_position;\n"
"out gl_PerVertex\n"
"{\n"
" vec4 gl_Position;\n"
"};\n\n"
"void main()\n"
"{\n"
" gl_Position = vec4(float(1 - 2 * int(gl_VertexIndex != 1)),\n"
" float(1 - 2 * int(gl_VertexIndex > 0)), 0.0, 1.0);\n"
"}\n");
programCollection.glslSources.add("frag") << glu::FragmentSource(
"#version 450\n"
"precision mediump int; precision highp float;"
"layout(location = 0) out highp vec4 o_color;\n"
"void main()\n"
"{\n"
" o_color = vec4(0.0, 1.0, 0.5, 1.0);\n"
"}\n");
}
}
TestInstance* PipelineLibraryMiscTestCase::createInstance(Context& context) const
{
return new PipelineLibraryMiscTestInstance(context, m_testParams);
}
} // anonymous
void addPipelineLibraryConfigurationsTests (tcu::TestCaseGroup* group, bool optimize)
{
const int R = -1;
const PipelineTreeConfiguration pipelineTreeConfiguration[] =
{
{ {
{ R, 4 }, /* 4 */
} },
{ {
{ R, 0 }, /* 0 */
/* / / \ \ */
{ 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 } /* 1 1 1 1 */
} },
{ {
{ R, 0 }, /* 0 */
/* / / \ */
{ 0, 1 }, { 0, 1 }, { 0, 2 } /* 1 1 2 */
} },
{ {
{ R, 0 }, /* 0 */
/* / / \ */
{ 0, 1 }, { 0, 2 }, { 0, 1 } /* 1 2 1 */
} },
{ {
{ R, 0 }, /* 0 */
/* / \ */
{ 0, 2 }, { 0, 2 }, /* 2 2 */
} },
{ {
{ R, 1 }, /* 1 */
/* / \ */
{ 0, 2 }, { 0, 1 }, /* 2 1 */
} },
{ {
{ R, 2 }, /* 2 */
/* / \ */
{ 0, 1 }, { 0, 1 }, /* 1 1 */
} },
{ {
{ R, 3 }, /* 3 */
/* / */
{ 0, 1 }, /* 1 */
} },
{ {
{ R, 1 }, /* 1 */
/* / */
{ 0, 3 }, /* 3 */
} },
{ {
{ R, 0 }, /* 0 */
/* / \ */
{ 0, 0 }, { 0, 0 }, /* 0 0 */
/* / \ / \ */
{ 1, 1 }, { 1, 1 }, { 2, 1 }, { 2, 1 }, /* 1 1 1 1 */
} },
{ {
{ R, 0 }, /* 0 */
/* / \ */
{ 0, 0 }, { 0, 1 }, /* 0 1 */
/* / \ \ */
{ 1, 1 }, { 1, 1 }, { 2, 1 }, /* 1 1 1 */
} },
{ {
{ R, 1 }, /* 1 */
/* / \ */
{ 0, 0 }, { 0, 1 }, /* 0 1 */
/* / \ */
{ 1, 1 }, { 1, 1 }, /* 1 1 */
} },
{ {
{ R, 1 }, /* 1 */
/* / */
{ 0, 1 }, /* 1 */
/* / \ */
{ 1, 1 }, { 1, 1 }, /* 1 1 */
} },
{ {
{ R, 1 }, /* 1 */
/* / */
{ 0, 1 }, /* 1 */
/* / */
{ 1, 1 }, /* 1 */
/* / */
{ 2, 1 }, /* 1 */
} },
{ {
{ R, 0 }, /* 0 */
/* / */
{ 0, 1 }, /* 1 */
/* / */
{ 1, 1 }, /* 1 */
/* / */
{ 2, 1 }, /* 1 */
/* / */
{ 3, 1 }, /* 1 */
} },
};
for (size_t libConfigNdx = 0; libConfigNdx < DE_LENGTH_OF_ARRAY(pipelineTreeConfiguration); ++libConfigNdx)
{
const bool delayedShaderCreate = (libConfigNdx != 0);
const TestParams testParams =
{
pipelineTreeConfiguration[libConfigNdx], // PipelineTreeConfiguration pipelineTreeConfiguration;
optimize, // bool optimize;
delayedShaderCreate // bool delayedShaderCreate;
};
const std::string testName = getTestName(pipelineTreeConfiguration[libConfigNdx]);
if (optimize && testParams.pipelineTreeConfiguration.size() == 1)
continue;
group->addChild(new PipelineLibraryTestCase(group->getTestContext(), testName.c_str(), "", testParams));
}
}
tcu::TestCaseGroup* createPipelineLibraryTests(tcu::TestContext& testCtx)
{
de::MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "graphics_library", "Tests verifying graphics pipeline libraries"));
addTestGroup(group.get(), "fast", "Tests graphics pipeline libraries linkage without optimization", addPipelineLibraryConfigurationsTests, false);
addTestGroup(group.get(), "optimize", "Tests graphics pipeline libraries linkage with optimization", addPipelineLibraryConfigurationsTests, true);
de::MovePtr<tcu::TestCaseGroup> miscTests(new tcu::TestCaseGroup(testCtx, "misc", "Miscellaneous graphics pipeline library tests"));
de::MovePtr<tcu::TestCaseGroup> independentLayoutSetsTests(new tcu::TestCaseGroup(testCtx, "independent_pipeline_layout_sets", ""));
independentLayoutSetsTests->addChild(new PipelineLibraryMiscTestCase(testCtx, "fast_linked", { MiscTestMode::INDEPENDENT_PIPELINE_LAYOUT_SETS_FAST_LINKED, 0u, 0u }));
independentLayoutSetsTests->addChild(new PipelineLibraryMiscTestCase(testCtx, "link_opt_union_handle", { MiscTestMode::INDEPENDENT_PIPELINE_LAYOUT_SETS_WITH_LINK_TIME_OPTIMIZATION_UNION_HANDLE, 0u, 0u }));
miscTests->addChild(independentLayoutSetsTests.release());
de::MovePtr<tcu::TestCaseGroup> bindNullDescriptorCombinationsTests(new tcu::TestCaseGroup(testCtx, "bind_null_descriptor_set", ""));
const std::vector<const char*> bindNullDescriptorCombinations
{
// note: there will be as many descriptor sets layouts in pipeline layout as there are chcaracters in the case name;
// '0' represents unused descriptor set layout, '1' represents used one;
// location of first '1' represents index of layout used in vertex shader;
// if present second '1' represents index of layout used in fragment shader
"1",
"11",
"01",
"10",
"101",
"1010",
"1001" // descriptor sets layouts for first pipeline part will be (&layoutA, NULL, NULL, NULL),
// for second pipeline part (NULL, NULL, NULL, &layoutB)
};
for (const char* name : bindNullDescriptorCombinations)
{
deUint32 layoutsCount = static_cast<deUint32>(strlen(name));
deUint32 layoutsBits = 0u;
// construct deUint32 with bits sets based on case name
for (deUint32 i = 0; i < layoutsCount; ++i)
layoutsBits |= (name[i] == '1') * (1 << (8 * sizeof(layoutsBits) - i - 1));
bindNullDescriptorCombinationsTests->addChild(new PipelineLibraryMiscTestCase(testCtx, name, { MiscTestMode::BIND_NULL_DESCRIPTOR_SET, layoutsCount, layoutsBits }));
}
miscTests->addChild(bindNullDescriptorCombinationsTests.release());
de::MovePtr<tcu::TestCaseGroup> otherTests(new tcu::TestCaseGroup(testCtx, "other", ""));
otherTests->addChild(new PipelineLibraryMiscTestCase(testCtx, "compare_link_times", { MiscTestMode::COMPARE_LINK_TIMES, 0u, 0u }));
otherTests->addChild(new PipelineLibraryMiscTestCase(testCtx, "null_descriptor_set_in_monolithic_pipeline", { MiscTestMode::BIND_NULL_DESCRIPTOR_SET_IN_MONOLITHIC_PIPELINE, 0u, 0u }));
miscTests->addChild(otherTests.release());
group->addChild(miscTests.release());
return group.release();
}
} // pipeline
} // vkt