blob: c306bf00c9b7713693468a04d878af2a640db78a [file] [log] [blame]
/*------------------------------------------------------------------------
* Vulkan Conformance Tests
* ------------------------
*
* Copyright (c) 2021 The Khronos Group Inc.
* Copyright (c) 2023 LunarG, Inc.
* Copyright (c) 2023 Nintendo
*
* 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 "tcuTextureUtil.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 "vkRayTracingUtil.hpp"
#include "vktTestCase.hpp"
#include "vktTestGroupUtil.hpp"
#include "vktCustomInstancesDevices.hpp"
#include "tcuCommandLine.hpp"
#include "tcuImageCompare.hpp"
#include "tcuTestLog.hpp"
#include "tcuRGBA.hpp"
#include "../draw/vktDrawCreateInfoUtil.hpp"
#include "deMath.h"
#include "deRandom.hpp"
#include "deClock.h"
#include <vector>
#include <chrono>
#include <set>
#include <limits>
namespace vkt
{
namespace pipeline
{
namespace
{
using namespace vk;
using namespace vkt;
using namespace tcu;
static const uint32_t RENDER_SIZE_WIDTH = 16u;
static const uint32_t 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 VkColorComponentFlags ALL_COLOR_COMPONENTS =
VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
static const int numClipDistances = 5;
static const int numCullDistances = 3;
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
{
int32_t parentIndex;
uint32_t shaderCount;
};
typedef std::vector<PipelineTreeNode> PipelineTreeConfiguration;
struct TestParams
{
PipelineTreeConfiguration pipelineTreeConfiguration;
bool optimize;
bool delayedShaderCreate;
bool useMaintenance5;
};
struct RuntimePipelineTreeNode
{
int32_t parentIndex;
VkGraphicsPipelineLibraryFlagsEXT graphicsPipelineLibraryFlags;
VkGraphicsPipelineLibraryFlagsEXT subtreeGraphicsPipelineLibraryFlags;
Move<VkPipeline> pipeline;
std::vector<VkPipeline> pipelineLibraries;
// We need to track the linked libraries too, included in VkPipelineLibraryCreateInfoKHR->pLibraries
std::vector<VkGraphicsPipelineLibraryFlagsEXT> linkedLibraryFlags;
};
typedef std::vector<RuntimePipelineTreeNode> RuntimePipelineTreeConfiguration;
inline UVec4 ivec2uvec(const IVec4 &ivec)
{
return UVec4{
static_cast<uint32_t>(ivec[0]),
static_cast<uint32_t>(ivec[1]),
static_cast<uint32_t>(ivec[2]),
static_cast<uint32_t>(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 VK_NULL_HANDLE;
}
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 uint32_t libraryCount = static_cast<uint32_t>(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, // uint32_t 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 uint32_t width, const uint32_t 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, // uint32_t mipLevels;
1u, // uint32_t arrayLayers;
VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
usage, // VkImageUsageFlags usage;
VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
0u, // uint32_t queueFamilyIndexCount;
DE_NULL, // const uint32_t* 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, // uint32_t baseMipLevel;
1, // uint32_t levelCount;
0, // uint32_t baseArrayLayer;
1, // uint32_t 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 uint32_t width, const uint32_t 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, // uint32_t mipLevels;
1u, // uint32_t arrayLayers;
VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
usage, // VkImageUsageFlags usage;
VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
0u, // uint32_t queueFamilyIndexCount;
DE_NULL, // const uint32_t* pQueueFamilyIndices;
VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
};
return imageInfo;
}
const VkFramebufferCreateInfo makeFramebufferCreateInfo(const VkRenderPass renderPass, const uint32_t attachmentCount,
const VkImageView *attachments, const uint32_t width,
const uint32_t height)
{
const VkFramebufferCreateInfo result = {
VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0, // VkFramebufferCreateFlags flags;
renderPass, // VkRenderPass renderPass;
attachmentCount, // uint32_t attachmentCount;
attachments, // const VkImageView* pAttachments;
width, // uint32_t width;
height, // uint32_t height;
1, // uint32_t 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;
false, // VkBool32 sampleShadingEnable;
0.0f, // float minSampleShading;
DE_NULL, // const VkSampleMask* pSampleMask;
false, // VkBool32 alphaToCoverageEnable;
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;
uint32_t m_shaderModuleCreateInfoCount;
VkShaderModuleCreateInfo m_shaderModuleCreateInfo[2];
std::vector<VkPipelineShaderStageCreateInfo> m_pipelineShaderStageCreateInfo;
Move<VkShaderModule> m_vertModule;
Move<VkShaderModule> m_fragModule;
Move<VkShaderModule> m_meshModule;
};
void updateVertexInputInterface(Context &context, GraphicsPipelineCreateInfo &graphicsPipelineCreateInfo,
VkPrimitiveTopology topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
uint32_t vertexDescriptionCount = 1u)
{
DE_UNREF(context);
graphicsPipelineCreateInfo.m_vertexInputBindingDescription = {
0u, // uint32_t binding;
sizeof(tcu::Vec4), // uint32_t strideInBytes;
VK_VERTEX_INPUT_RATE_VERTEX, // VkVertexInputRate inputRate;
};
graphicsPipelineCreateInfo.m_vertexInputAttributeDescription = {
0u, // uint32_t location;
0u, // uint32_t binding;
VK_FORMAT_R32G32B32A32_SFLOAT, // VkFormat format;
0u // uint32_t offsetInBytes;
};
const VkPipelineVertexInputStateCreateInfo vertexInputStateCreateInfo{
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkPipelineVertexInputStateCreateFlags flags;
vertexDescriptionCount, // uint32_t vertexBindingDescriptionCount;
&graphicsPipelineCreateInfo
.m_vertexInputBindingDescription, // const VkVertexInputBindingDescription* pVertexBindingDescriptions;
vertexDescriptionCount, // uint32_t 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, bool useDynamicViewPort = false, bool useMeshShader = false,
VkPolygonMode polygonMode = VK_POLYGON_MODE_FILL,
const VkSpecializationInfo *specializationInfo = DE_NULL)
{
const std::string shaderName = (useMeshShader ? "mesh" : "vert");
const ProgramBinary &shaderBinary = context.getBinaryCollection().get(shaderName);
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;
(uintptr_t)shaderBinary.getSize(), // uintptr_t codeSize;
(uint32_t *)shaderBinary.getBinary(), // const uint32_t* pCode;
};
if (!delayedShaderCreate)
{
const DeviceInterface &vk = context.getDeviceInterface();
const VkDevice device = context.getDevice();
Move<VkShaderModule> shaderMod = createShaderModule(vk, device, &shaderModuleCreateInfo);
if (useMeshShader)
graphicsPipelineCreateInfo.m_meshModule = shaderMod;
else
graphicsPipelineCreateInfo.m_vertModule = shaderMod;
}
const void *pNext = delayedShaderCreate ? &shaderModuleCreateInfo : DE_NULL;
const VkShaderModule shaderModule =
delayedShaderCreate ?
VK_NULL_HANDLE :
(useMeshShader ? *graphicsPipelineCreateInfo.m_meshModule : *graphicsPipelineCreateInfo.m_vertModule);
const VkPipelineShaderStageCreateInfo pipelineShaderStageCreateInfo = {
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // VkStructureType sType;
pNext, // const void* pNext;
0u, // VkPipelineShaderStageCreateFlags flags;
(useMeshShader ? VK_SHADER_STAGE_MESH_BIT_EXT // VkShaderStageFlagBits stage;
:
VK_SHADER_STAGE_VERTEX_BIT),
shaderModule, // VkShaderModule module;
"main", // const char* pName;
specializationInfo // 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, // uint32_t viewportCount;
&viewport, // const VkViewport* pViewports;
1u, // uint32_t scissorCount;
&scissor // const VkRect2D* pScissors;
};
std::vector<VkDynamicState> dynamicStates = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR};
const VkPipelineDynamicStateCreateInfo pipelineDynamicState = {
VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, // VkStructureType sType;
nullptr, // const void* pNext;
0u, // VkPipelineDynamicStateCreateFlags flags;
de::sizeU32(dynamicStates), // uint32_t dynamicStateCount;
de::dataOrNull(dynamicStates) // const VkDynamicState* pDynamicStates;
};
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);
if (useDynamicViewPort)
graphicsPipelineCreateInfo.addState(pipelineDynamicState);
}
void updatePostRasterization(Context &context, GraphicsPipelineCreateInfo &graphicsPipelineCreateInfo,
bool delayedShaderCreate, bool enableDepth = true,
const VkSpecializationInfo *specializationInfo = DE_NULL)
{
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;
(uintptr_t)shaderBinary.getSize(), // uintptr_t codeSize;
(uint32_t *)shaderBinary.getBinary(), // const uint32_t* 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 ? VK_NULL_HANDLE : *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;
specializationInfo // 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, // uint32_t compareMask;
0u, // uint32_t writeMask;
0u, // uint32_t 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, // uint32_t compareMask;
0u, // uint32_t writeMask;
0u, // uint32_t 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, // uint32_t 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 != VK_NULL_HANDLE)
builder.addIndexedBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1u, VK_SHADER_STAGE_VERTEX_BIT, 0u, DE_NULL);
if (fragShaderBuffer != VK_NULL_HANDLE)
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, // uint32_t descriptorSetCount;
&layout // const VkDescriptorSetLayout* pSetLayouts;
};
Move<VkDescriptorSet> descriptorSet = allocateDescriptorSet(vk, device, &allocInfo);
DescriptorSetUpdateBuilder builder;
if (vertShaderBuffer != VK_NULL_HANDLE)
{
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 != VK_NULL_HANDLE)
{
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;
}
VkFormat getSupportedDepthFormat(const InstanceInterface &vk, const VkPhysicalDevice physicalDevice)
{
VkFormatProperties properties;
const VkFormat DepthFormats[] = {VK_FORMAT_D32_SFLOAT, VK_FORMAT_X8_D24_UNORM_PACK32, VK_FORMAT_D24_UNORM_S8_UINT,
VK_FORMAT_D32_SFLOAT_S8_UINT};
for (const auto format : DepthFormats)
{
vk.getPhysicalDeviceFormatProperties(physicalDevice, format, &properties);
if (properties.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)
return format;
}
TCU_THROW(NotSupportedError, "Depth format is not supported");
}
bool PipelineLibraryTestInstance::runTest(RuntimePipelineTreeConfiguration &runtimePipelineTreeConfiguration,
const bool optimize, const bool delayedShaderCreate)
{
const DeviceInterface &vk = m_context.getDeviceInterface();
const VkDevice device = m_context.getDevice();
const uint32_t 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 =
getSupportedDepthFormat(m_context.getInstanceInterface(), m_context.getPhysicalDevice());
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 int32_t nodeNdxLast = static_cast<int32_t>(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, VK_NULL_HANDLE);
const Move<VkDescriptorSetLayout> descriptorSetLayoutFrag =
createDescriptorSetLayout(VK_NULL_HANDLE, **paletteBuffer);
const Move<VkDescriptorSetLayout> descriptorSetLayoutBoth =
createDescriptorSetLayout(**zCoordBuffer, **paletteBuffer);
const Move<VkDescriptorSet> descriptorSetVert =
createDescriptorSet(*descriptorPool, *descriptorSetLayoutVert, **zCoordBuffer, VK_NULL_HANDLE);
const Move<VkDescriptorSet> descriptorSetFrag =
createDescriptorSet(*descriptorPool, *descriptorSetLayoutFrag, VK_NULL_HANDLE, **paletteBuffer);
VkDescriptorSet vecDescriptorSetBoth[2] = {*descriptorSetVert, *descriptorSetFrag};
VkDescriptorSetLayout vecLayoutVert[2] = {*descriptorSetLayoutVert, VK_NULL_HANDLE};
VkDescriptorSetLayout vecLayoutFrag[2] = {VK_NULL_HANDLE, *descriptorSetLayoutFrag};
VkDescriptorSetLayout vecLayoutBoth[2] = {*descriptorSetLayoutVert, *descriptorSetLayoutFrag};
VkPipelineLayoutCreateFlags pipelineLayoutCreateFlag = 0u;
if (!m_data.useMaintenance5 && (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 (int32_t 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 :
VK_NULL_HANDLE;
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");
}
}
}
VkGraphicsPipelineLibraryFlagsEXT linkedLibrariesFlags = 0;
for (auto flag : node.linkedLibraryFlags)
linkedLibrariesFlags |= flag;
// When pLibraries have any pipeline library with fragment shader state and current pipeline we try to create doesn't,
// we need to set a MS info.
if ((linkedLibrariesFlags & VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT) &&
!(node.graphicsPipelineLibraryFlags & VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT) &&
(graphicsPipelineCreateInfo.pMultisampleState == DE_NULL))
{
const VkPipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo =
makePipelineMultisampleStateCreateInfo();
graphicsPipelineCreateInfo.addState(pipelineMultisampleStateCreateInfo);
}
if (!m_data.useMaintenance5 && linkedLibrariesFlags != ALL_GRAPHICS_PIPELINE_LIBRARY_FLAGS &&
graphicsPipelineLibraryCreateInfo.flags != 0)
appendStructurePtrToVulkanChain(&graphicsPipelineCreateInfo.pNext, &graphicsPipelineLibraryCreateInfo);
if (linkingInfo.libraryCount != 0)
{
appendStructurePtrToVulkanChain(&graphicsPipelineCreateInfo.pNext, &linkingInfo);
graphicsPipelineCreateInfo.layout = *pipelineLayoutSame;
}
linkedLibrariesFlags |= node.graphicsPipelineLibraryFlags;
// if current pipeline that we try to create and pLibraries have all states of pipelines, we are not allowed to create a pipeline library.
if (linkedLibrariesFlags == ALL_GRAPHICS_PIPELINE_LIBRARY_FLAGS)
{
DE_ASSERT(!buildLibrary);
graphicsPipelineCreateInfo.flags &= ~VK_PIPELINE_CREATE_LIBRARY_BIT_KHR;
}
node.pipeline = createGraphicsPipeline(vk, device, VK_NULL_HANDLE, &graphicsPipelineCreateInfo);
if (buildLibrary)
{
DE_ASSERT(de::inBounds(node.parentIndex, 0, static_cast<int32_t>(runtimePipelineTreeConfiguration.size())));
runtimePipelineTreeConfiguration[node.parentIndex].pipelineLibraries.push_back(*node.pipeline);
runtimePipelineTreeConfiguration[node.parentIndex].linkedLibraryFlags.push_back(linkedLibrariesFlags);
}
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 uint32_t vertexCount = static_cast<uint32_t>(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 uint32_t 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 = {(int32_t)renderSize.x(), (int32_t)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<uint32_t>(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);
uint32_t permutationId = 0;
std::set<uint32_t> was;
bool result = true;
do
{
RuntimePipelineTreeConfiguration runtimePipelineTreeConfiguration(m_data.pipelineTreeConfiguration.size());
size_t subsetNdxStart = 0;
uint32_t uniqueTreeSubsetCode = 0;
for (size_t nodeNdx = 0; nodeNdx < runtimePipelineTreeConfiguration.size(); ++nodeNdx)
{
const uint32_t 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 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 TestParams data)
: vkt::TestCase(context, name)
, m_data(data)
{
}
PipelineLibraryTestCase::~PipelineLibraryTestCase(void)
{
}
void PipelineLibraryTestCase::checkSupport(Context &context) const
{
if (m_data.useMaintenance5)
{
context.requireDeviceFunctionality("VK_KHR_maintenance5");
return;
}
context.requireDeviceFunctionality("VK_KHR_pipeline_library");
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,
SHADER_MODULE_CREATE_INFO_COMP,
SHADER_MODULE_CREATE_INFO_RT,
SHADER_MODULE_CREATE_INFO_RT_LIB,
NULL_RENDERING_CREATE_INFO,
COMMON_FRAG_LIBRARY,
VIEW_INDEX_FROM_DEVICE_INDEX_IN_ALL_STAGES,
VIEW_INDEX_FROM_DEVICE_INDEX_IN_MESH_STAGES,
};
struct MiscTestParams
{
MiscTestMode mode;
// attributes used in BIND_NULL_DESCRIPTOR_SET mode
uint32_t layoutsCount;
uint32_t 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);
tcu::TestStatus runCommonFragLibraryTest(void);
struct VerificationData
{
const tcu::IVec2 point;
const tcu::IVec4 color;
};
tcu::TestStatus verifyResult(const std::vector<VerificationData> &verificationData,
const tcu::ConstPixelBufferAccess &colorPixelAccess) const;
// verification for test mode: COMMON_FRAG_LIBRARY_FAST_LINKED
bool verifyOnePipelineLibraryResult(const tcu::ConstPixelBufferAccess &colorPixelAccess, const int numBars) 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 uint32_t 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();
else if (m_testParams.mode == MiscTestMode::COMMON_FRAG_LIBRARY)
return runCommonFragLibraryTest();
DE_ASSERT(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 (uint32_t 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 uint32_t maxBitsCount = 8 * sizeof(m_testParams.layoutsBits);
VkDescriptorSetLayout vertDescriptorSetLayouts[maxBitsCount];
VkDescriptorSetLayout fragDescriptorSetLayouts[maxBitsCount];
VkDescriptorSetLayout allDescriptorSetLayouts[maxBitsCount];
// set all layouts to NULL
deMemset(&vertDescriptorSetLayouts, 0, maxBitsCount * sizeof(VkDescriptorSetLayout));
deMemset(&fragDescriptorSetLayouts, 0, 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 (uint32_t 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<uint32_t> bitsThatAreSet;
for (uint32_t i = 0u; i < m_testParams.layoutsCount; ++i)
{
if (m_testParams.layoutsBits & (1 << (maxBitsCount - 1 - i)))
bitsThatAreSet.push_back(i);
}
uint32_t usedDescriptorSets = static_cast<uint32_t>(bitsThatAreSet.size());
DE_ASSERT(usedDescriptorSets && (usedDescriptorSets < 3u));
uint32_t vertSetIndex = bitsThatAreSet[0];
uint32_t 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);
}
else
{
pipelineLayoutCreateInfo.setLayoutCount = 0u;
pipelineLayoutCreateInfo.pSetLayouts = DE_NULL;
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 uint32_t commonPipelinePartFlags = uint32_t(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, 0u);
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, VK_NULL_HANDLE, &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, VK_NULL_HANDLE, &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, VK_NULL_HANDLE, &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_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_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{(int32_t)m_renderArea.extent.width, (int32_t)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
int32_t width = (int32_t)m_renderArea.extent.width;
int32_t height = (int32_t)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[] = {VK_NULL_HANDLE, *descriptorSet};
VkDescriptorSetLayout rawDescriptorSetLayouts[] = {VK_NULL_HANDLE, *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, VK_NULL_HANDLE, &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{(int32_t)m_renderArea.extent.width, (int32_t)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
int32_t width = (int32_t)m_renderArea.extent.width;
int32_t height = (int32_t)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 (uint32_t 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
uint32_t allLayoutsFlag = uint32_t(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], VK_NULL_HANDLE,
*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 = uint32_t(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);
uint32_t commonPipelinePartFlags = uint32_t(VK_PIPELINE_CREATE_LIBRARY_BIT_KHR);
uint32_t finalPipelineFlag = 0u;
if (useLinkTimeOptimization)
{
commonPipelinePartFlags |= uint32_t(VK_PIPELINE_CREATE_RETAIN_LINK_TIME_OPTIMIZATION_INFO_BIT_EXT);
finalPipelineFlag = uint32_t(VK_PIPELINE_CREATE_LINK_TIME_OPTIMIZATION_BIT_EXT);
}
GraphicsPipelineCreateInfo partialPipelineCreateInfo[]{{VK_NULL_HANDLE, *m_renderPass, 0, commonPipelinePartFlags},
{*vertLayouts, *m_renderPass, 0, commonPipelinePartFlags},
{*fragLayouts, *m_renderPass, 0, commonPipelinePartFlags},
{VK_NULL_HANDLE, *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, VK_NULL_HANDLE);
std::vector<Move<VkPipeline>> pipelineParts;
pipelineParts.reserve(4u);
VkGraphicsPipelineLibraryCreateInfoEXT libraryCreateInfo =
makeGraphicsPipelineLibraryCreateInfo(VK_GRAPHICS_PIPELINE_LIBRARY_VERTEX_INPUT_INTERFACE_BIT_EXT);
for (uint32_t i = 0; i < 4u; ++i)
{
libraryCreateInfo.flags = GRAPHICS_PIPELINE_LIBRARY_FLAGS[i];
appendStructurePtrToVulkanChain(&partialPipelineCreateInfo[i].pNext, &libraryCreateInfo);
pipelineParts.emplace_back(createGraphicsPipeline(vk, device, VK_NULL_HANDLE, &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, VK_NULL_HANDLE, &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{(int32_t)m_renderArea.extent.width, (int32_t)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
int32_t width = (int32_t)m_renderArea.extent.width;
int32_t height = (int32_t)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 uint32_t uniqueLibrariesCount = 2u;
const uint32_t 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},
};
de::Random rnd(static_cast<uint32_t>(deGetMicroseconds()));
const uint32_t vertexRandSpecConsts[] = {rnd.getUint32() * 2, rnd.getUint32() * 2};
const uint32_t fragmentRandSpecConsts[] = {rnd.getUint32() * 2, rnd.getUint32() * 2};
const VkSpecializationMapEntry entry = {
0, // uint32_t constantID;
0, // uint32_t offset;
sizeof(int32_t) // size_t size;
};
const VkSpecializationInfo vertexSpecializationInfos[] = {
{
1u, // uint32_t mapEntryCount;
&entry, // const VkSpecializationMapEntry* pMapEntries;
sizeof(int32_t), // size_t dataSize;
&vertexRandSpecConsts[0] // const void* pData;
},
{
1u, // uint32_t mapEntryCount;
&entry, // const VkSpecializationMapEntry* pMapEntries;
sizeof(int32_t), // size_t dataSize;
&vertexRandSpecConsts[1] // const void* pData;
}};
const VkSpecializationInfo fragmentSpecializationInfos[] = {
{
1u, // uint32_t mapEntryCount;
&entry, // const VkSpecializationMapEntry* pMapEntries;
sizeof(int32_t), // size_t dataSize;
&fragmentRandSpecConsts[0] // const void* pData;
},
{
1u, // uint32_t mapEntryCount;
&entry, // const VkSpecializationMapEntry* pMapEntries;
sizeof(int32_t), // size_t dataSize;
&fragmentRandSpecConsts[1] // const void* pData;
}};
// 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, false, false, VK_POLYGON_MODE_FILL,
&vertexSpecializationInfos[0]);
updatePreRasterization(m_context, partialPipelineCreateInfo[3], false, false, false, VK_POLYGON_MODE_LINE,
&vertexSpecializationInfos[1]);
updatePostRasterization(m_context, partialPipelineCreateInfo[4], false, true, &fragmentSpecializationInfos[0]);
updatePostRasterization(m_context, partialPipelineCreateInfo[5], false, false, &fragmentSpecializationInfos[1]);
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<int64_t, std::nano> creationDuration;
};
std::vector<PipelinePartData> pipelinePartData(pipelinesCount);
VkGraphicsPipelineLibraryCreateInfoEXT libraryCreateInfo =
makeGraphicsPipelineLibraryCreateInfo(VK_GRAPHICS_PIPELINE_LIBRARY_VERTEX_INPUT_INTERFACE_BIT_EXT);
for (uint32_t 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, VK_NULL_HANDLE, partialPipelineCreateInfo + i);
partData.creationDuration = std::chrono::high_resolution_clock::now() - timeStart;
}
// iterate over all combinations of parts
for (uint32_t i = 0u; i < (uint32_t)dePow(4, uniqueLibrariesCount); ++i)
{
// select new unique combination of parts
uint32_t vertexInputIndex = (i) % 2;
uint32_t preRasterizationIndex = (i / 2) % 2;
uint32_t fragmentStateIndex = (i / 4) % 2;
uint32_t 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, VK_NULL_HANDLE, &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");
}
/*
Middle bar should contain clip distance with linear values between 0 and 1.
Cull distance is always 0.5 when enabled.
*/
void makeReferenceImage(tcu::PixelBufferAccess &reference, IVec2 clipRegion, const int numBars, int barIdx,
const tcu::Vec4 &clipAreaColor)
{
for (int y = 0; y < reference.getHeight(); ++y)
for (int x = 0; x < reference.getWidth(); ++x)
{
if (x < clipRegion.x() && y < clipRegion.y())
{
reference.setPixel(clipAreaColor, x, y);
continue;
}
const int barWidth = reference.getWidth() / numBars;
const bool insideBar = x >= barWidth * barIdx && x < barWidth * (barIdx + 1);
const float expectedClipDistance =
insideBar ? (((((float)y + 0.5f) / (float)reference.getHeight()) - 0.5f) * 2.0f) : 0.0f;
float expectedCullDistance = 0.5f;
const float height = (float)reference.getHeight();
if (y >= (reference.getHeight() / 2))
expectedCullDistance = expectedCullDistance * (1.0f + (2.0f * (float)y) - height) / height;
else
expectedCullDistance = 0.0f;
const tcu::Vec4 expectedColor = Vec4(1.0, expectedClipDistance, expectedCullDistance, 1.0);
reference.setPixel(expectedColor, x, y);
}
}
de::MovePtr<BufferWithMemory> makeVertexBuffer(const DeviceInterface &vk, const VkDevice device, Allocator &allocator,
std::vector<tcu::Vec4> &vertexData, VkBufferUsageFlagBits usageFlags)
{
const size_t bufferDataSize = de::dataSize(vertexData);
const VkBufferCreateInfo bufferCreateInfo = makeBufferCreateInfo(bufferDataSize, usageFlags);
de::MovePtr<BufferWithMemory> buffer = de::MovePtr<BufferWithMemory>(
new BufferWithMemory(vk, device, allocator, bufferCreateInfo, MemoryRequirement::HostVisible));
deMemcpy(buffer->getAllocation().getHostPtr(), vertexData.data(), bufferDataSize);
flushAlloc(vk, device, buffer->getAllocation());
return buffer;
}
/*
Pipeline libraries:
Compile a fragment only pipeline library L1.
Compile a mesh only pipeline library L2.
Compile a vertex only pipeline library L3.
Fast link L2 & L1.
Fast link L3 & L1.
Shaders:
Vertex and mesh shaders write clip distance and cull distance.
Fragment shader reads clip distance and cull distance.
Clip and cull tests taken from vktClippingTests.
*/
tcu::TestStatus PipelineLibraryMiscTestInstance::runCommonFragLibraryTest(void)
{
const DeviceInterface &vk = m_context.getDeviceInterface();
const VkDevice device = m_context.getDevice();
Allocator &allocator = m_context.getDefaultAllocator();
// create output buffer for verification
const VkDeviceSize outputBufferDataSize = static_cast<VkDeviceSize>(
m_renderArea.extent.width * m_renderArea.extent.height * tcu::getPixelSize(mapVkFormat(m_colorFormat)));
const VkBufferCreateInfo outputBufferCreateInfo = makeBufferCreateInfo(
outputBufferDataSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT);
const BufferWithMemory outputBuffer(vk, device, allocator, outputBufferCreateInfo, MemoryRequirement::HostVisible);
const int numBars = numClipDistances + numCullDistances;
// vertex shader input
std::vector<Vec4> vertices;
{
const float dx = 2.0f / numBars;
for (int i = 0; i < numBars; ++i)
{
const float x = -1.0f + dx * static_cast<float>(i);
vertices.push_back(Vec4(x, -1.0f, 0.0f, 1.0f));
vertices.push_back(Vec4(x, 1.0f, 0.0f, 1.0f));
vertices.push_back(Vec4(x + dx, -1.0f, 0.0f, 1.0f));
vertices.push_back(Vec4(x, 1.0f, 0.0f, 1.0f));
vertices.push_back(Vec4(x + dx, 1.0f, 0.0f, 1.0f));
vertices.push_back(Vec4(x + dx, -1.0f, 0.0f, 1.0f));
}
}
const auto vertexBufferStages = (VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_MESH_BIT_EXT);
const auto vertexBufferSize = static_cast<VkDeviceSize>(de::dataSize(vertices));
const auto vertexCount = de::sizeU32(vertices);
const auto vertexBufferUsage = (VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT);
const auto vertexBufferLoc = DescriptorSetUpdateBuilder::Location::binding(0u);
const auto vertexBufferType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
// Vertex buffer.
const de::MovePtr<BufferWithMemory> vertexBuffer =
makeVertexBuffer(vk, device, allocator, vertices, (VkBufferUsageFlagBits)vertexBufferUsage);
// for the link time opt (and when null handle is used) use total pipeline layout recreated without the INDEPENDENT SETS bit
const auto allLayoutsFlag = VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT;
// Set layout.
DescriptorSetLayoutBuilder layoutBuilder;
layoutBuilder.addSingleBinding(vertexBufferType, vertexBufferStages);
const auto descriptorSetLayout = layoutBuilder.build(vk, device);
// Descriptor pool.
DescriptorPoolBuilder poolBuilder;
poolBuilder.addType(vertexBufferType);
const auto descriptorPool = poolBuilder.build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
// Descriptor set.
const auto descriptorSet = makeDescriptorSet(vk, device, descriptorPool.get(), descriptorSetLayout.get());
// Update descriptor set.
DescriptorSetUpdateBuilder updateBuilder;
const auto vertexBufferDescInfo = makeDescriptorBufferInfo(vertexBuffer->get(), 0ull, vertexBufferSize);
updateBuilder.writeSingle(descriptorSet.get(), vertexBufferLoc, vertexBufferType, &vertexBufferDescInfo);
updateBuilder.update(vk, device);
// Setup pipeline libraries
VkDescriptorSet allDescriptorSets[] = {*descriptorSet};
VkDescriptorSetLayout meshDescriptorSetLayouts[] = {*descriptorSetLayout};
VkDescriptorSetLayout allDescriptorSetLayouts[] = {*descriptorSetLayout};
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = initVulkanStructure();
pipelineLayoutCreateInfo.flags = allLayoutsFlag;
pipelineLayoutCreateInfo.setLayoutCount = 1u;
pipelineLayoutCreateInfo.pSetLayouts = allDescriptorSetLayouts;
Move<VkPipelineLayout> allLayouts = createPipelineLayout(vk, device, &pipelineLayoutCreateInfo);
pipelineLayoutCreateInfo.pSetLayouts = meshDescriptorSetLayouts;
Move<VkPipelineLayout> meshLayouts = createPipelineLayout(vk, device, &pipelineLayoutCreateInfo);
pipelineLayoutCreateInfo.setLayoutCount = 0u;
pipelineLayoutCreateInfo.pSetLayouts = nullptr;
Move<VkPipelineLayout> vertLayouts = createPipelineLayout(vk, device, &pipelineLayoutCreateInfo);
Move<VkPipelineLayout> fragLayouts = createPipelineLayout(vk, device, &pipelineLayoutCreateInfo);
Move<VkPipelineLayout> nullLayout = createPipelineLayout(vk, device, &pipelineLayoutCreateInfo);
const uint32_t commonPipelinePartFlags = uint32_t(VK_PIPELINE_CREATE_LIBRARY_BIT_KHR);
enum
{
PIPELINE_CREATEINFO_IDX_VII = 0,
PIPELINE_CREATEINFO_IDX_PRERAST_VERT,
PIPELINE_CREATEINFO_IDX_PRERAST_MESH,
PIPELINE_CREATEINFO_IDX_POSTRAST,
PIPELINE_CREATEINFO_IDX_FO,
PIPELINE_CREATEINFO_IDX_MAX
};
const VkGraphicsPipelineLibraryFlagBitsEXT map_pipeline_createinfo_to_flags[PIPELINE_CREATEINFO_IDX_MAX] = {
VK_GRAPHICS_PIPELINE_LIBRARY_VERTEX_INPUT_INTERFACE_BIT_EXT,
VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT, // pre-rasterization (vert)
VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT, // pre-rasterization (mesh)
VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT,
VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT};
GraphicsPipelineCreateInfo allPipelineCreateInfos[]{
{VK_NULL_HANDLE, *m_renderPass, 0,
commonPipelinePartFlags}, // [PIPELINE_CREATEINFO_IDX_VII]: vertex input interface
{*vertLayouts, *m_renderPass, 0,
commonPipelinePartFlags}, // [PIPELINE_CREATEINFO_IDX_PRERAST_VERT]: pre-rasterization (vert)
{*meshLayouts, *m_renderPass, 0,
commonPipelinePartFlags}, // [PIPELINE_CREATEINFO_IDX_PRERAST_MESH]: pre-rasterization (mesh)
{*fragLayouts, *m_renderPass, 0,
commonPipelinePartFlags}, // [PIPELINE_CREATEINFO_IDX_POSTRAST]: post-rasterization (frag)
{VK_NULL_HANDLE, *m_renderPass, 0,
commonPipelinePartFlags}, // [PIPELINE_CREATEINFO_IDX_FO]: frag output interface
};
// initialize VkGraphicsPipelineLibraryCreateInfoEXT for each library
std::vector<VkGraphicsPipelineLibraryCreateInfoEXT> libraryCreateInfos;
for (uint32_t i = 0; i < PIPELINE_CREATEINFO_IDX_MAX; i++)
{
VkGraphicsPipelineLibraryFlagBitsEXT flag = map_pipeline_createinfo_to_flags[i];
libraryCreateInfos.push_back(makeGraphicsPipelineLibraryCreateInfo(flag));
}
// vertex-only pipeline parts
uint32_t pipelineCreateInfoIdx = PIPELINE_CREATEINFO_IDX_VII;
updateVertexInputInterface(m_context, allPipelineCreateInfos[pipelineCreateInfoIdx],
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 1u);
appendStructurePtrToVulkanChain(&allPipelineCreateInfos[pipelineCreateInfoIdx].pNext,
&libraryCreateInfos[pipelineCreateInfoIdx]);
pipelineCreateInfoIdx = PIPELINE_CREATEINFO_IDX_PRERAST_VERT;
updatePreRasterization(m_context, allPipelineCreateInfos[pipelineCreateInfoIdx], false, true);
appendStructurePtrToVulkanChain(&allPipelineCreateInfos[pipelineCreateInfoIdx].pNext,
&libraryCreateInfos[pipelineCreateInfoIdx]);
// mesh-only pipeline parts
pipelineCreateInfoIdx = PIPELINE_CREATEINFO_IDX_PRERAST_MESH;
updatePreRasterization(m_context, allPipelineCreateInfos[pipelineCreateInfoIdx], false, true, true);
appendStructurePtrToVulkanChain(&allPipelineCreateInfos[pipelineCreateInfoIdx].pNext,
&libraryCreateInfos[pipelineCreateInfoIdx]);
// fragment-only pipeline parts, always linked
pipelineCreateInfoIdx = PIPELINE_CREATEINFO_IDX_POSTRAST;
updatePostRasterization(m_context, allPipelineCreateInfos[PIPELINE_CREATEINFO_IDX_POSTRAST], false, false);
appendStructurePtrToVulkanChain(&allPipelineCreateInfos[pipelineCreateInfoIdx].pNext,
&libraryCreateInfos[pipelineCreateInfoIdx]);
pipelineCreateInfoIdx = PIPELINE_CREATEINFO_IDX_FO;
updateFragmentOutputInterface(m_context, allPipelineCreateInfos[pipelineCreateInfoIdx], ALL_COLOR_COMPONENTS);
appendStructurePtrToVulkanChain(&allPipelineCreateInfos[pipelineCreateInfoIdx].pNext,
&libraryCreateInfos[pipelineCreateInfoIdx]);
// final pipeline libraries, pipelines[0]: vertex+frag and pipelines[1]: mesh+frag
std::vector<Move<VkPipeline>> pipelines;
pipelines.reserve(2u);
enum
{
PIPELINE_LIB_VERT_FRAG = 0,
PIPELINE_LIB_MESH_FRAG,
PIPELINE_LIB_MAX
};
// create parts of each of the two final pipelines and then create the final pipelines
std::vector<VkPipeline> rawParts[PIPELINE_LIB_MAX];
std::vector<Move<VkPipeline>> pipelineParts[PIPELINE_LIB_MAX];
for (uint32_t combo = PIPELINE_LIB_VERT_FRAG; combo < PIPELINE_LIB_MAX; combo++)
{
uint32_t numParts = 0;
std::vector<uint32_t> createInfoIndices;
VkGraphicsPipelineCreateInfo finalPipelineInfo = initVulkanStructure();
finalPipelineInfo.flags = 0u;
if (combo == PIPELINE_LIB_VERT_FRAG)
{
// pipeline parts are 4 for vertex+frag pipeline
// vertex inout interface, pre-rasterization (vertex), post-rasterization, frag output interface
numParts = 4u;
finalPipelineInfo.layout = *nullLayout;
createInfoIndices.push_back(PIPELINE_CREATEINFO_IDX_VII);
createInfoIndices.push_back(PIPELINE_CREATEINFO_IDX_PRERAST_VERT);
createInfoIndices.push_back(PIPELINE_CREATEINFO_IDX_POSTRAST);
createInfoIndices.push_back(PIPELINE_CREATEINFO_IDX_FO);
}
else
{
// pipeline parts are 3 for mesh+frag pipeline
// pre-rasterization (mesh), post-rasterization, frag output interface
numParts = 3u;
finalPipelineInfo.layout = *allLayouts;
createInfoIndices.push_back(PIPELINE_CREATEINFO_IDX_PRERAST_MESH);
createInfoIndices.push_back(PIPELINE_CREATEINFO_IDX_POSTRAST);
createInfoIndices.push_back(PIPELINE_CREATEINFO_IDX_FO);
}
// extend pNext chain and create all partial pipelines
rawParts[combo].resize(numParts, VK_NULL_HANDLE);
pipelineParts[combo].reserve(numParts);
uint32_t partsIdx = 0;
for (const auto &idx : createInfoIndices)
{
pipelineParts[combo].emplace_back(
createGraphicsPipeline(vk, device, VK_NULL_HANDLE, &allPipelineCreateInfos[idx]));
rawParts[combo][partsIdx] = *(pipelineParts[combo][partsIdx]);
partsIdx++;
}
// create final pipeline out of the parts
VkPipelineLibraryCreateInfoKHR linkingInfo = makePipelineLibraryCreateInfo(rawParts[combo]);
appendStructurePtrToVulkanChain(&finalPipelineInfo.pNext, &linkingInfo);
pipelines.emplace_back(createGraphicsPipeline(vk, device, VK_NULL_HANDLE, &finalPipelineInfo));
}
// execute both pipelines one after the other and verify the result of each
bool testOk = true;
const VkViewport viewport = makeViewport(m_renderArea.extent.width, m_renderArea.extent.height);
const VkRect2D scissor = makeRect2D(m_renderArea.extent.width, m_renderArea.extent.height);
for (uint32_t combo = PIPELINE_LIB_VERT_FRAG; (combo < PIPELINE_LIB_MAX) && (testOk != false); combo++)
{
// only the render pass is shared between the two pipelines
const VkImageCreateInfo colorImageCreateInfo =
makeColorImageCreateInfo(m_colorFormat, m_renderArea.extent.width, m_renderArea.extent.height);
de::MovePtr<ImageWithMemory> localColorImage = de::MovePtr<ImageWithMemory>(
new ImageWithMemory(vk, device, allocator, colorImageCreateInfo, MemoryRequirement::Any));
const VkImageViewCreateInfo colorImageViewCreateInfo = makeImageViewCreateInfo(
**localColorImage, m_colorFormat, static_cast<VkImageAspectFlags>(VK_IMAGE_ASPECT_COLOR_BIT));
const Move<VkImageView> colorImageView = createImageView(vk, device, &colorImageViewCreateInfo);
const VkFramebufferCreateInfo framebufferCreateInfo = makeFramebufferCreateInfo(
*m_renderPass, 1u, &*colorImageView, m_renderArea.extent.width, m_renderArea.extent.height);
Move<VkFramebuffer> localFramebuffer = createFramebuffer(vk, device, &framebufferCreateInfo);
Move<VkCommandBuffer> localCmdBuffer =
allocateCommandBuffer(vk, device, *m_cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
vk::beginCommandBuffer(vk, *localCmdBuffer, 0u);
{
const VkDeviceSize zeroOffset = 0ull;
beginRenderPass(vk, *localCmdBuffer, *m_renderPass, *localFramebuffer, m_renderArea, m_colorClearColor);
vk.cmdBindPipeline(*localCmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelines[combo]);
vk.cmdSetViewport(*localCmdBuffer, 0, 1, &viewport);
vk.cmdSetScissor(*localCmdBuffer, 0, 1, &scissor);
if (combo == PIPELINE_LIB_VERT_FRAG)
{
vk.cmdBindVertexBuffers(*localCmdBuffer, 0u, 1u, &vertexBuffer->get(), &zeroOffset);
vk.cmdDraw(*localCmdBuffer, vertexCount, 1u, 0u, 0u);
}
else
{
vk.cmdBindDescriptorSets(*localCmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *allLayouts, 0u, 1u,
allDescriptorSets, 0u, nullptr);
uint32_t num_workgroups = 1u;
vk.cmdDrawMeshTasksEXT(*localCmdBuffer, num_workgroups, 1u, 1u);
}
endRenderPass(vk, *localCmdBuffer);
const tcu::IVec2 size{(int32_t)m_renderArea.extent.width, (int32_t)m_renderArea.extent.height};
copyImageToBuffer(vk, *localCmdBuffer, **localColorImage, *outputBuffer, size);
}
vk::endCommandBuffer(vk, *localCmdBuffer);
vk::submitCommandsAndWait(vk, device, m_context.getUniversalQueue(), *localCmdBuffer);
{
vk::invalidateAlloc(vk, device, outputBuffer.getAllocation());
const tcu::TextureFormat tcuFormat = vk::mapVkFormat(m_colorFormat);
const tcu::ConstPixelBufferAccess resultAccess(tcuFormat, m_renderArea.extent.width,
m_renderArea.extent.height, 1,
outputBuffer.getAllocation().getHostPtr());
testOk = verifyOnePipelineLibraryResult(resultAccess, numBars);
}
}
return (testOk == true ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Rendered image(s) are incorrect"));
}
tcu::TestStatus PipelineLibraryMiscTestInstance::verifyResult(const std::vector<VerificationData> &verificationData,
const tcu::ConstPixelBufferAccess &colorPixelAccess) const
{
const int32_t 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 (uint32_t 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");
}
bool PipelineLibraryMiscTestInstance::verifyOnePipelineLibraryResult(const tcu::ConstPixelBufferAccess &resultAccess,
const int numBars) const
{
bool testOk = true;
tcu::TestLog &log = m_context.getTestContext().getLog();
const tcu::TextureFormat tcuFormat = vk::mapVkFormat(m_colorFormat);
tcu::TextureLevel referenceLevel(tcuFormat, m_renderArea.extent.width, m_renderArea.extent.height);
auto referenceAccess = referenceLevel.getAccess();
const tcu::Vec4 bgColor = Vec4(1.0f, 0.0f, 0.0f, 1.0f); // red
const tcu::Vec4 clipAreaColor = Vec4(0.0f, 0.0f, 0.0f, 1.0f); // black
const IVec2 clipRegion =
IVec2(numClipDistances * m_renderArea.extent.width / numBars, m_renderArea.extent.height / 2);
tcu::clear(referenceAccess, bgColor);
makeReferenceImage(referenceAccess, clipRegion, numBars, numClipDistances / 2, clipAreaColor);
const float colorThres = 0.005f; // 1/255 < 0.005 < 2/255
const tcu::Vec4 threshold(0.0f, colorThres, colorThres, 0.0f);
if (!tcu::floatThresholdCompare(log, "Result", "Reference", referenceAccess, resultAccess, threshold,
tcu::COMPARE_LOG_ON_ERROR))
testOk = false;
return testOk;
}
class PipelineLibraryShaderModuleInfoInstance : public TestInstance
{
public:
PipelineLibraryShaderModuleInfoInstance(Context &context)
: TestInstance(context)
, m_vkd(m_context.getDeviceInterface())
, m_device(m_context.getDevice())
, m_alloc(m_context.getDefaultAllocator())
, m_queueIndex(m_context.getUniversalQueueFamilyIndex())
, m_queue(m_context.getUniversalQueue())
, m_outVector(kOutputBufferElements, std::numeric_limits<uint32_t>::max())
, m_cmdBuffer(DE_NULL)
{
}
virtual ~PipelineLibraryShaderModuleInfoInstance(void)
{
}
static constexpr size_t kOutputBufferElements = 64u;
protected:
void prepareOutputBuffer(VkShaderStageFlags stages);
void allocateCmdBuffers(void);
void addModule(const std::string &moduleName, VkShaderStageFlagBits stage);