blob: aea31a966e3866f9087b96dae7a468aa3b33a458 [file] [log] [blame]
/*------------------------------------------------------------------------
* Vulkan Conformance Tests
* ------------------------
*
* Copyright (c) 2019 The Khronos Group Inc.
* Copyright (c) 2019 Valve Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*//*!
* \file
* \brief Pipeline Cache Tests
*//*--------------------------------------------------------------------*/
#include "vktPipelineCreationFeedbackTests.hpp"
#include "vktPipelineVertexUtil.hpp"
#include "vktTestCase.hpp"
#include "vktTestCaseUtil.hpp"
#include "vkMemUtil.hpp"
#include "vkBuilderUtil.hpp"
#include "vkRefUtil.hpp"
#include "vkQueryUtil.hpp"
#include "vkTypeUtil.hpp"
#include "vkObjUtil.hpp"
#include "tcuTestLog.hpp"
#include <sstream>
#include <vector>
namespace vkt
{
namespace pipeline
{
using namespace vk;
namespace
{
enum
{
VK_MAX_SHADER_STAGES = 6,
};
enum
{
VK_MAX_PIPELINE_PARTS = 5, // 4 parts + 1 final
};
enum
{
PIPELINE_CACHE_NDX_NO_CACHE = 0,
PIPELINE_CACHE_NDX_DERIVATIVE = 1,
PIPELINE_CACHE_NDX_CACHED = 2,
PIPELINE_CACHE_NDX_COUNT,
};
// helper functions
std::string getShaderFlagStr (const VkShaderStageFlags shader,
bool isDescription)
{
std::ostringstream desc;
if (shader & VK_SHADER_STAGE_COMPUTE_BIT)
{
desc << ((isDescription) ? "compute stage" : "compute_stage");
}
else
{
desc << ((isDescription) ? "vertex stage" : "vertex_stage");
if (shader & VK_SHADER_STAGE_GEOMETRY_BIT)
desc << ((isDescription) ? " geometry stage" : "_geometry_stage");
if (shader & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT)
desc << ((isDescription) ? " tessellation control stage" : "_tessellation_control_stage");
if (shader & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)
desc << ((isDescription) ? " tessellation evaluation stage" : "_tessellation_evaluation_stage");
desc << ((isDescription) ? " fragment stage" : "_fragment_stage");
}
return desc.str();
}
std::string getCaseStr (const deUint32 ndx)
{
switch(ndx)
{
case PIPELINE_CACHE_NDX_NO_CACHE:
return "No cached pipeline";
case PIPELINE_CACHE_NDX_CACHED:
return "Cached pipeline";
case PIPELINE_CACHE_NDX_DERIVATIVE:
return "Pipeline derivative";
default:
DE_FATAL("Unknown case!");
}
return "Unknown case";
}
// helper classes
class CacheTestParam
{
public:
CacheTestParam (const PipelineConstructionType pipelineConstructionType,
const VkShaderStageFlags shaders,
deBool noCache,
deBool delayedDestroy);
virtual ~CacheTestParam (void) = default;
virtual const std::string generateTestName (void) const;
virtual const std::string generateTestDescription (void) const;
PipelineConstructionType getPipelineConstructionType (void) const { return m_pipelineConstructionType; }
VkShaderStageFlags getShaderFlags (void) const { return m_shaders; }
deBool isCacheDisabled (void) const { return m_noCache; }
deBool isDelayedDestroy (void) const { return m_delayedDestroy; }
protected:
PipelineConstructionType m_pipelineConstructionType;
VkShaderStageFlags m_shaders;
bool m_noCache;
bool m_delayedDestroy;
};
CacheTestParam::CacheTestParam (const PipelineConstructionType pipelineConstructionType, const VkShaderStageFlags shaders, deBool noCache, deBool delayedDestroy)
: m_pipelineConstructionType (pipelineConstructionType)
, m_shaders (shaders)
, m_noCache (noCache)
, m_delayedDestroy (delayedDestroy)
{
}
const std::string CacheTestParam::generateTestName (void) const
{
std::string cacheString [] = { "", "_no_cache" };
std::string delayedDestroyString [] = { "", "_delayed_destroy" };
return getShaderFlagStr(m_shaders, false) + cacheString[m_noCache ? 1 : 0] + delayedDestroyString[m_delayedDestroy ? 1 : 0];
}
const std::string CacheTestParam::generateTestDescription (void) const
{
std::string result("Get pipeline creation feedback with " + getShaderFlagStr(m_shaders, true));
if (m_noCache)
result += " with no cache";
if (m_delayedDestroy)
result += " with delayed destroy";
return result;
}
template <class Test>
vkt::TestCase* newTestCase (tcu::TestContext& testContext,
const CacheTestParam* testParam)
{
return new Test(testContext,
testParam->generateTestName().c_str(),
testParam->generateTestDescription().c_str(),
testParam);
}
// Test Classes
class CacheTest : public vkt::TestCase
{
public:
CacheTest(tcu::TestContext& testContext,
const std::string& name,
const std::string& description,
const CacheTestParam* param)
: vkt::TestCase (testContext, name, description)
, m_param (*param)
{ }
virtual ~CacheTest (void) { }
protected:
const CacheTestParam m_param;
};
class CacheTestInstance : public vkt::TestInstance
{
public:
CacheTestInstance (Context& context,
const CacheTestParam* param);
virtual ~CacheTestInstance (void);
virtual tcu::TestStatus iterate (void);
protected:
virtual tcu::TestStatus verifyTestResult (void) = 0;
protected:
const CacheTestParam* m_param;
Move<VkPipelineCache> m_cache;
deBool m_extensions;
};
CacheTestInstance::CacheTestInstance (Context& context,
const CacheTestParam* param)
: TestInstance (context)
, m_param (param)
, m_extensions (m_context.requireDeviceFunctionality("VK_EXT_pipeline_creation_feedback"))
{
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice vkDevice = m_context.getDevice();
if (m_param->isCacheDisabled() == DE_FALSE)
{
const VkPipelineCacheCreateInfo pipelineCacheCreateInfo =
{
VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkPipelineCacheCreateFlags flags;
0u, // deUintptr initialDataSize;
DE_NULL, // const void* pInitialData;
};
m_cache = createPipelineCache(vk, vkDevice, &pipelineCacheCreateInfo);
}
}
CacheTestInstance::~CacheTestInstance (void)
{
}
tcu::TestStatus CacheTestInstance::iterate (void)
{
return verifyTestResult();
}
class GraphicsCacheTest : public CacheTest
{
public:
GraphicsCacheTest (tcu::TestContext& testContext,
const std::string& name,
const std::string& description,
const CacheTestParam* param)
: CacheTest (testContext, name, description, param)
{ }
virtual ~GraphicsCacheTest (void) { }
virtual void initPrograms (SourceCollections& programCollection) const;
virtual void checkSupport (Context& context) const;
virtual TestInstance* createInstance (Context& context) const;
};
class GraphicsCacheTestInstance : public CacheTestInstance
{
public:
GraphicsCacheTestInstance (Context& context,
const CacheTestParam* param);
virtual ~GraphicsCacheTestInstance (void);
protected:
void preparePipelineWrapper (GraphicsPipelineWrapper& gpw,
VkShaderModule vertShaderModule,
VkShaderModule tescShaderModule,
VkShaderModule teseShaderModule,
VkShaderModule geomShaderModule,
VkShaderModule fragShaderModule,
VkPipelineCreationFeedbackEXT* pipelineCreationFeedback,
VkPipelineCreationFeedbackEXT* pipelineStageCreationFeedbacks,
VkPipeline basePipelineHandle);
virtual tcu::TestStatus verifyTestResult (void);
protected:
const tcu::UVec2 m_renderSize;
const VkFormat m_colorFormat;
const VkFormat m_depthFormat;
Move<VkPipelineLayout> m_pipelineLayout;
Move<VkRenderPass> m_renderPass;
GraphicsPipelineWrapper m_pipeline[PIPELINE_CACHE_NDX_COUNT];
VkPipelineCreationFeedbackEXT m_pipelineCreationFeedback[VK_MAX_PIPELINE_PARTS * PIPELINE_CACHE_NDX_COUNT];
VkPipelineCreationFeedbackEXT m_pipelineStageCreationFeedbacks[PIPELINE_CACHE_NDX_COUNT * VK_MAX_SHADER_STAGES];
};
void GraphicsCacheTest::initPrograms (SourceCollections& programCollection) const
{
programCollection.glslSources.add("color_vert_1") << glu::VertexSource(
"#version 310 es\n"
"layout(location = 0) in vec4 position;\n"
"layout(location = 1) in vec4 color;\n"
"layout(location = 0) out highp vec4 vtxColor;\n"
"void main (void)\n"
"{\n"
" gl_Position = position;\n"
" vtxColor = color;\n"
"}\n");
programCollection.glslSources.add("color_vert_2") << glu::VertexSource(
"#version 310 es\n"
"layout(location = 0) in vec4 position;\n"
"layout(location = 1) in vec4 color;\n"
"layout(location = 0) out highp vec4 vtxColor;\n"
"void main (void)\n"
"{\n"
" gl_Position = position;\n"
" gl_PointSize = 1.0f;\n"
" vtxColor = color + vec4(0.1, 0.2, 0.3, 0.0);\n"
"}\n");
programCollection.glslSources.add("color_frag") << glu::FragmentSource(
"#version 310 es\n"
"layout(location = 0) in highp vec4 vtxColor;\n"
"layout(location = 0) out highp vec4 fragColor;\n"
"void main (void)\n"
"{\n"
" fragColor = vtxColor;\n"
"}\n");
VkShaderStageFlags shaderFlag = m_param.getShaderFlags();
if (shaderFlag & VK_SHADER_STAGE_GEOMETRY_BIT)
{
programCollection.glslSources.add("unused_geo") << glu::GeometrySource(
"#version 450 \n"
"layout(triangles) in;\n"
"layout(triangle_strip, max_vertices = 3) out;\n"
"layout(location = 0) in highp vec4 in_vtxColor[];\n"
"layout(location = 0) out highp vec4 vtxColor;\n"
"out gl_PerVertex { vec4 gl_Position; float gl_PointSize; };\n"
"in gl_PerVertex { vec4 gl_Position; float gl_PointSize; } gl_in[];\n"
"void main (void)\n"
"{\n"
" for(int ndx=0; ndx<3; ndx++)\n"
" {\n"
" gl_Position = gl_in[ndx].gl_Position;\n"
" gl_PointSize = gl_in[ndx].gl_PointSize;\n"
" vtxColor = in_vtxColor[ndx];\n"
" EmitVertex();\n"
" }\n"
" EndPrimitive();\n"
"}\n");
}
if (shaderFlag & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT)
{
programCollection.glslSources.add("basic_tcs") << glu::TessellationControlSource(
"#version 450 \n"
"layout(vertices = 3) out;\n"
"layout(location = 0) in highp vec4 color[];\n"
"layout(location = 0) out highp vec4 vtxColor[];\n"
"out gl_PerVertex { vec4 gl_Position; float gl_PointSize; } gl_out[3];\n"
"in gl_PerVertex { vec4 gl_Position; float gl_PointSize; } gl_in[gl_MaxPatchVertices];\n"
"void main()\n"
"{\n"
" gl_TessLevelOuter[0] = 4.0;\n"
" gl_TessLevelOuter[1] = 4.0;\n"
" gl_TessLevelOuter[2] = 4.0;\n"
" gl_TessLevelInner[0] = 4.0;\n"
" gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
" gl_out[gl_InvocationID].gl_PointSize = gl_in[gl_InvocationID].gl_PointSize;\n"
" vtxColor[gl_InvocationID] = color[gl_InvocationID];\n"
"}\n");
}
if (shaderFlag & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)
{
programCollection.glslSources.add("basic_tes") << glu::TessellationEvaluationSource(
"#version 450 \n"
"layout(triangles, fractional_even_spacing, ccw) in;\n"
"layout(location = 0) in highp vec4 colors[];\n"
"layout(location = 0) out highp vec4 vtxColor;\n"
"out gl_PerVertex { vec4 gl_Position; float gl_PointSize; };\n"
"in gl_PerVertex { vec4 gl_Position; float gl_PointSize; } gl_in[gl_MaxPatchVertices];\n"
"void main() \n"
"{\n"
" float u = gl_TessCoord.x;\n"
" float v = gl_TessCoord.y;\n"
" float w = gl_TessCoord.z;\n"
" vec4 pos = vec4(0);\n"
" vec4 color = vec4(0);\n"
" pos.xyz += u * gl_in[0].gl_Position.xyz;\n"
" color.xyz += u * colors[0].xyz;\n"
" pos.xyz += v * gl_in[1].gl_Position.xyz;\n"
" color.xyz += v * colors[1].xyz;\n"
" pos.xyz += w * gl_in[2].gl_Position.xyz;\n"
" color.xyz += w * colors[2].xyz;\n"
" pos.w = 1.0;\n"
" color.w = 1.0;\n"
" gl_Position = pos;\n"
" gl_PointSize = gl_in[0].gl_PointSize;"
" vtxColor = color;\n"
"}\n");
}
}
void GraphicsCacheTest::checkSupport (Context& context) const
{
if (m_param.getShaderFlags() & VK_SHADER_STAGE_GEOMETRY_BIT)
context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_GEOMETRY_SHADER);
if ((m_param.getShaderFlags() & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) ||
(m_param.getShaderFlags() & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT))
context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_TESSELLATION_SHADER);
checkPipelineLibraryRequirements(context.getInstanceInterface(), context.getPhysicalDevice(), m_param.getPipelineConstructionType());
}
TestInstance* GraphicsCacheTest::createInstance (Context& context) const
{
return new GraphicsCacheTestInstance(context, &m_param);
}
GraphicsCacheTestInstance::GraphicsCacheTestInstance (Context& context,
const CacheTestParam* param)
: CacheTestInstance (context, param)
, m_renderSize (32u, 32u)
, m_colorFormat (VK_FORMAT_R8G8B8A8_UNORM)
, m_depthFormat (VK_FORMAT_D16_UNORM)
, m_pipeline
{
{ context.getDeviceInterface(), context.getDevice(), param->getPipelineConstructionType(), VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT },
{ context.getDeviceInterface(), context.getDevice(), param->getPipelineConstructionType(), VK_PIPELINE_CREATE_DERIVATIVE_BIT },
{ context.getDeviceInterface(), context.getDevice(), param->getPipelineConstructionType(), VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT },
}
{
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice vkDevice = m_context.getDevice();
deMemset(m_pipelineCreationFeedback, 0, sizeof(VkPipelineCreationFeedbackEXT) * VK_MAX_PIPELINE_PARTS * PIPELINE_CACHE_NDX_COUNT);
deMemset(m_pipelineStageCreationFeedbacks, 0, sizeof(VkPipelineCreationFeedbackEXT) * PIPELINE_CACHE_NDX_COUNT * VK_MAX_SHADER_STAGES);
// Create pipeline layout
{
const VkPipelineLayoutCreateInfo pipelineLayoutParams =
{
VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkPipelineLayoutCreateFlags flags;
0u, // deUint32 setLayoutCount;
DE_NULL, // const VkDescriptorSetLayout* pSetLayouts;
0u, // deUint32 pushConstantRangeCount;
DE_NULL // const VkPushConstantRange* pPushConstantRanges;
};
m_pipelineLayout = createPipelineLayout(vk, vkDevice, &pipelineLayoutParams);
}
// Create render pass
m_renderPass = makeRenderPass(vk, vkDevice, m_colorFormat, m_depthFormat);
// Create shader modules
Move<VkShaderModule> vertShaderModule1 = createShaderModule(vk, vkDevice, context.getBinaryCollection().get("color_vert_1"), 0);
Move<VkShaderModule> vertShaderModule2 = createShaderModule(vk, vkDevice, context.getBinaryCollection().get("color_vert_2"), 0);
Move<VkShaderModule> fragShaderModule = createShaderModule(vk, vkDevice, context.getBinaryCollection().get("color_frag"), 0);
Move<VkShaderModule> tescShaderModule;
Move<VkShaderModule> teseShaderModule;
Move<VkShaderModule> geomShaderModule;
VkShaderStageFlags shaderFlags = m_param->getShaderFlags();
if (shaderFlags & VK_SHADER_STAGE_GEOMETRY_BIT)
geomShaderModule = createShaderModule(vk, vkDevice, context.getBinaryCollection().get("unused_geo"), 0);
if (shaderFlags & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT)
tescShaderModule = createShaderModule(vk, vkDevice, context.getBinaryCollection().get("basic_tcs"), 0);
if (shaderFlags & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)
teseShaderModule = createShaderModule(vk, vkDevice, context.getBinaryCollection().get("basic_tes"), 0);
for (deUint32 ndx = 0; ndx < PIPELINE_CACHE_NDX_COUNT; ndx++)
{
VkShaderModule vertShaderModule = (ndx == PIPELINE_CACHE_NDX_DERIVATIVE) ? *vertShaderModule2 : *vertShaderModule1;
if (ndx == PIPELINE_CACHE_NDX_CACHED && !param->isDelayedDestroy())
{
// Destroy the NO_CACHE pipeline to check that the cached one really hits cache,
// except for the case where we're testing cache hit of a pipeline still active.
m_pipeline[PIPELINE_CACHE_NDX_NO_CACHE].destroyPipeline();
}
preparePipelineWrapper(m_pipeline[ndx], vertShaderModule, *tescShaderModule, *teseShaderModule, *geomShaderModule, *fragShaderModule,
&m_pipelineCreationFeedback[VK_MAX_PIPELINE_PARTS * ndx],
&m_pipelineStageCreationFeedbacks[VK_MAX_SHADER_STAGES * ndx],
ndx == PIPELINE_CACHE_NDX_DERIVATIVE ? m_pipeline[PIPELINE_CACHE_NDX_NO_CACHE].getPipeline() : DE_NULL);
if (ndx != PIPELINE_CACHE_NDX_NO_CACHE)
{
// Destroy the pipeline as soon as it is created, except the NO_CACHE because
// it is needed as a base pipeline for the derivative case.
m_pipeline[ndx].destroyPipeline();
if (ndx == PIPELINE_CACHE_NDX_CACHED && param->isDelayedDestroy())
{
// Destroy the pipeline we didn't destroy earlier for the isDelayedDestroy case.
m_pipeline[PIPELINE_CACHE_NDX_NO_CACHE].destroyPipeline();
}
}
}
}
GraphicsCacheTestInstance::~GraphicsCacheTestInstance (void)
{
}
void GraphicsCacheTestInstance::preparePipelineWrapper (GraphicsPipelineWrapper& gpw,
VkShaderModule vertShaderModule,
VkShaderModule tescShaderModule,
VkShaderModule teseShaderModule,
VkShaderModule geomShaderModule,
VkShaderModule fragShaderModule,
VkPipelineCreationFeedbackEXT* pipelineCreationFeedback,
VkPipelineCreationFeedbackEXT* pipelineStageCreationFeedbacks,
VkPipeline basePipelineHandle)
{
const VkVertexInputBindingDescription vertexInputBindingDescription
{
0u, // deUint32 binding;
sizeof(Vertex4RGBA), // deUint32 strideInBytes;
VK_VERTEX_INPUT_RATE_VERTEX, // VkVertexInputRate inputRate;
};
const VkVertexInputAttributeDescription vertexInputAttributeDescriptions[2]
{
{
0u, // deUint32 location;
0u, // deUint32 binding;
VK_FORMAT_R32G32B32A32_SFLOAT, // VkFormat format;
0u // deUint32 offsetInBytes;
},
{
1u, // deUint32 location;
0u, // deUint32 binding;
VK_FORMAT_R32G32B32A32_SFLOAT, // VkFormat format;
DE_OFFSET_OF(Vertex4RGBA, color), // deUint32 offsetInBytes;
}
};
const VkPipelineVertexInputStateCreateInfo vertexInputStateParams
{
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkPipelineVertexInputStateCreateFlags flags;
1u, // deUint32 vertexBindingDescriptionCount;
&vertexInputBindingDescription, // const VkVertexInputBindingDescription* pVertexBindingDescriptions;
2u, // deUint32 vertexAttributeDescriptionCount;
vertexInputAttributeDescriptions, // const VkVertexInputAttributeDescription* pVertexAttributeDescriptions;
};
const std::vector<VkViewport> viewport { makeViewport(m_renderSize) };
const std::vector<VkRect2D> scissor { makeRect2D(m_renderSize) };
const VkPipelineColorBlendAttachmentState colorBlendAttachmentState
{
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;
VK_COLOR_COMPONENT_R_BIT |
VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT |
VK_COLOR_COMPONENT_A_BIT // VkColorComponentFlags colorWriteMask;
};
const VkPipelineColorBlendStateCreateInfo colorBlendStateParams
{
VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkPipelineColorBlendStateCreateFlags flags;
VK_FALSE, // VkBool32 logicOpEnable;
VK_LOGIC_OP_COPY, // VkLogicOp logicOp;
1u, // deUint32 attachmentCount;
&colorBlendAttachmentState, // const VkPipelineColorBlendAttachmentState* pAttachments;
{ 0.0f, 0.0f, 0.0f, 0.0f }, // float blendConst[4];
};
VkPipelineDepthStencilStateCreateInfo depthStencilStateParams
{
VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkPipelineDepthStencilStateCreateFlags flags;
VK_TRUE, // VkBool32 depthTestEnable;
VK_TRUE, // VkBool32 depthWriteEnable;
VK_COMPARE_OP_LESS_OR_EQUAL, // VkCompareOp depthCompareOp;
VK_FALSE, // VkBool32 depthBoundsTestEnable;
VK_FALSE, // VkBool32 stencilTestEnable;
// VkStencilOpState front;
{
VK_STENCIL_OP_KEEP, // VkStencilOp failOp;
VK_STENCIL_OP_KEEP, // VkStencilOp passOp;
VK_STENCIL_OP_KEEP, // VkStencilOp depthFailOp;
VK_COMPARE_OP_NEVER, // VkCompareOp compareOp;
0u, // deUint32 compareMask;
0u, // deUint32 writeMask;
0u, // deUint32 reference;
},
// VkStencilOpState back;
{
VK_STENCIL_OP_KEEP, // VkStencilOp failOp;
VK_STENCIL_OP_KEEP, // VkStencilOp passOp;
VK_STENCIL_OP_KEEP, // VkStencilOp depthFailOp;
VK_COMPARE_OP_NEVER, // VkCompareOp compareOp;
0u, // deUint32 compareMask;
0u, // deUint32 writeMask;
0u, // deUint32 reference;
},
0.0f, // float minDepthBounds;
1.0f, // float maxDepthBounds;
};
VkPipelineCreationFeedbackCreateInfoEXT pipelineCreationFeedbackCreateInfo[VK_MAX_PIPELINE_PARTS];
PipelineCreationFeedbackCreateInfoWrapper pipelineCreationFeedbackWrapper[VK_MAX_PIPELINE_PARTS];
for (deUint32 i = 0u ; i < VK_MAX_PIPELINE_PARTS ; ++i)
{
pipelineCreationFeedbackCreateInfo[i] = initVulkanStructure();
pipelineCreationFeedbackCreateInfo[i].pPipelineCreationFeedback = &pipelineCreationFeedback[i];
pipelineCreationFeedbackWrapper[i].ptr = &pipelineCreationFeedbackCreateInfo[i];
}
deUint32 geometryStages = 1u + (geomShaderModule != DE_NULL) + (tescShaderModule != DE_NULL) + (teseShaderModule != DE_NULL);
if (m_param->getPipelineConstructionType() == PIPELINE_CONSTRUCTION_TYPE_MONOLITHIC)
{
pipelineCreationFeedbackCreateInfo[4].pipelineStageCreationFeedbackCount = 1u + geometryStages;
pipelineCreationFeedbackCreateInfo[4].pPipelineStageCreationFeedbacks = pipelineStageCreationFeedbacks;
}
else
{
// setup proper stages count for CreationFeedback structures
// that will be passed to pre-rasterization and fragment shader states
pipelineCreationFeedbackCreateInfo[1].pipelineStageCreationFeedbackCount = geometryStages;
pipelineCreationFeedbackCreateInfo[1].pPipelineStageCreationFeedbacks = pipelineStageCreationFeedbacks;
pipelineCreationFeedbackCreateInfo[2].pipelineStageCreationFeedbackCount = 1u;
pipelineCreationFeedbackCreateInfo[2].pPipelineStageCreationFeedbacks = pipelineStageCreationFeedbacks + geometryStages;
if (m_param->getPipelineConstructionType() == PIPELINE_CONSTRUCTION_TYPE_LINK_TIME_OPTIMIZED_LIBRARY)
{
pipelineCreationFeedbackCreateInfo[4].pipelineStageCreationFeedbackCount = 1u + geometryStages;
pipelineCreationFeedbackCreateInfo[4].pPipelineStageCreationFeedbacks = pipelineStageCreationFeedbacks;
}
}
gpw.setDefaultTopology((tescShaderModule == DE_NULL) ? VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST : VK_PRIMITIVE_TOPOLOGY_PATCH_LIST)
.setDefaultRasterizationState()
.setDefaultMultisampleState()
.setupVertexInputStete(&vertexInputStateParams, DE_NULL, *m_cache, pipelineCreationFeedbackWrapper[0])
.setupPreRasterizationShaderState(
viewport,
scissor,
*m_pipelineLayout,
*m_renderPass,
0u,
vertShaderModule,
DE_NULL,
tescShaderModule,
teseShaderModule,
geomShaderModule,
DE_NULL,
PipelineRenderingCreateInfoWrapper(),
*m_cache,
pipelineCreationFeedbackWrapper[1])
.setupFragmentShaderState(
*m_pipelineLayout,
*m_renderPass,
0u,
fragShaderModule,
&depthStencilStateParams,
DE_NULL,
DE_NULL,
DE_NULL,
*m_cache,
pipelineCreationFeedbackWrapper[2])
.setupFragmentOutputState(*m_renderPass, 0u, &colorBlendStateParams, DE_NULL, *m_cache, pipelineCreationFeedbackWrapper[3])
.setMonolithicPipelineLayout(*m_pipelineLayout)
.buildPipeline(*m_cache, basePipelineHandle, basePipelineHandle != DE_NULL ? -1 : 0, pipelineCreationFeedbackWrapper[4]);
}
tcu::TestStatus GraphicsCacheTestInstance::verifyTestResult (void)
{
tcu::TestLog& log = m_context.getTestContext().getLog();
bool durationZeroWarning = DE_FALSE;
bool cachedPipelineWarning = DE_FALSE;
bool isMonolithic = m_param->getPipelineConstructionType() == PIPELINE_CONSTRUCTION_TYPE_MONOLITHIC;
deUint32 finalPipelineIndex = deUint32(VK_MAX_PIPELINE_PARTS) - 1u;
deUint32 start = isMonolithic ? finalPipelineIndex : 0u;
deUint32 step = start + 1u;
// Iterate ofer creation feedback for all pipeline parts - if monolithic pipeline is tested then skip (step over) feedback for parts
for (deUint32 creationFeedbackNdx = start; creationFeedbackNdx < VK_MAX_PIPELINE_PARTS * PIPELINE_CACHE_NDX_COUNT; creationFeedbackNdx += step)
{
deUint32 pipelineCacheNdx = creationFeedbackNdx / deUint32(VK_MAX_PIPELINE_PARTS);
auto creationFeedbackFlags = m_pipelineCreationFeedback[creationFeedbackNdx].flags;
std::string caseString = getCaseStr(pipelineCacheNdx);
std::ostringstream message;
message << caseString;
// Check first that the no cached pipeline was missed in the pipeline cache
if (!(creationFeedbackFlags & VK_PIPELINE_CREATION_FEEDBACK_VALID_BIT_EXT))
{
message << ": invalid data";
return tcu::TestStatus::fail(message.str());
}
if (m_param->isCacheDisabled() && creationFeedbackFlags & VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT_EXT)
{
message << ": feedback indicates pipeline hit cache when it shouldn't";
return tcu::TestStatus::fail(message.str());
}
if (pipelineCacheNdx == PIPELINE_CACHE_NDX_NO_CACHE && creationFeedbackFlags & VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT_EXT)
{
message << ": hit the cache when it shouldn't";
return tcu::TestStatus::fail(message.str());
}
if (pipelineCacheNdx != PIPELINE_CACHE_NDX_DERIVATIVE && creationFeedbackFlags & VK_PIPELINE_CREATION_FEEDBACK_BASE_PIPELINE_ACCELERATION_BIT_EXT)
{
message << ": feedback indicates base pipeline acceleration when it shouldn't";
return tcu::TestStatus::fail(message.str());
}
deUint32 pipelinePartIndex = creationFeedbackNdx % deUint32(VK_MAX_PIPELINE_PARTS);
if (pipelineCacheNdx == PIPELINE_CACHE_NDX_CACHED && !m_param->isCacheDisabled() && (creationFeedbackFlags & VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT_EXT) == 0)
{
// For graphics pipeline library cache is only hit for the pre_rasterization and fragment_shader stages
if (isMonolithic || (pipelinePartIndex == 1u) || (pipelinePartIndex == 2u))
{
message << "\nWarning: Cached pipeline case did not hit the cache";
cachedPipelineWarning = DE_TRUE;
}
}
if (m_pipelineCreationFeedback[creationFeedbackNdx].duration == 0)
{
message << "\nWarning: Pipeline creation feedback reports duration spent creating a pipeline was zero nanoseconds\n";
durationZeroWarning = DE_TRUE;
}
message << "\n";
message << "\t\t Hit cache ? \t\t\t" << (creationFeedbackFlags & VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT_EXT ? "yes" : "no") << "\n";
message << "\t\t Base Pipeline Acceleration ? \t" << (creationFeedbackFlags & VK_PIPELINE_CREATION_FEEDBACK_BASE_PIPELINE_ACCELERATION_BIT_EXT ? "yes" : "no") << "\n";
message << "\t\t Duration (ns): \t\t" << m_pipelineCreationFeedback[creationFeedbackNdx].duration << "\n";
// dont repeat checking shader feedback for pipeline parts - just check all shaders when checkin final pipelines
if (pipelinePartIndex == finalPipelineIndex)
{
VkShaderStageFlags testedShaderFlags = m_param->getShaderFlags();
deUint32 shaderCount = 2u + ((testedShaderFlags & VK_SHADER_STAGE_GEOMETRY_BIT) != 0) +
((testedShaderFlags & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) != 0) +
((testedShaderFlags & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) != 0);
for(deUint32 shader = 0; shader < shaderCount; shader++)
{
const deUint32 index = VK_MAX_SHADER_STAGES * pipelineCacheNdx + shader;
message << "\t" <<(shader + 1) << " shader stage\n";
if (!(m_pipelineStageCreationFeedbacks[index].flags & VK_PIPELINE_CREATION_FEEDBACK_VALID_BIT_EXT))
{
if (m_pipelineStageCreationFeedbacks[index].flags)
{
std::ostringstream errorMsg;
errorMsg << caseString << ": Creation feedback is not valid for " << (shader + 1) << " shader stage but there are other flags written";
return tcu::TestStatus::fail(errorMsg.str());
}
message << "\t\t Pipeline Creation Feedback data is not valid\n";
continue;
}
if (m_param->isCacheDisabled() && m_pipelineStageCreationFeedbacks[index].flags & VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT_EXT)
{
std::ostringstream errorMsg;
errorMsg << caseString << ": feedback indicates pipeline " << (shader + 1) << " shader stage hit cache when it shouldn't";
return tcu::TestStatus::fail(errorMsg.str());
}
if (pipelineCacheNdx == PIPELINE_CACHE_NDX_CACHED && !m_param->isCacheDisabled() && (m_pipelineStageCreationFeedbacks[index].flags & VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT_EXT) == 0)
{
message << "Warning: pipeline stage did not hit the cache\n";
cachedPipelineWarning = DE_TRUE;
}
if (cachedPipelineWarning && m_pipelineStageCreationFeedbacks[index].flags & VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT_EXT)
{
// We only set the warning when the pipeline nor the pipeline stages hit the cache. If any of them did, them disable the warning.
cachedPipelineWarning = DE_FALSE;
}
message << "\t\t Hit cache ? \t\t\t" << (m_pipelineStageCreationFeedbacks[index].flags & VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT_EXT ? "yes" : "no") << "\n";
message << "\t\t Base Pipeline Acceleration ? \t" << (m_pipelineStageCreationFeedbacks[index].flags & VK_PIPELINE_CREATION_FEEDBACK_BASE_PIPELINE_ACCELERATION_BIT_EXT ? "yes" : "no") << "\n";
message << "\t\t Duration (ns): \t\t" << m_pipelineStageCreationFeedbacks[index].duration << "\n";
}
}
log << tcu::TestLog::Message << message.str() << tcu::TestLog::EndMessage;
}
if (cachedPipelineWarning)
{
return tcu::TestStatus(QP_TEST_RESULT_QUALITY_WARNING, "Cached pipeline or stage did not hit the cache");
}
if (durationZeroWarning)
{
return tcu::TestStatus(QP_TEST_RESULT_QUALITY_WARNING, "Pipeline creation feedback reports duration spent creating a pipeline was zero nanoseconds");
}
return tcu::TestStatus::pass("Pass");
}
class ComputeCacheTest : public CacheTest
{
public:
ComputeCacheTest (tcu::TestContext& testContext,
const std::string& name,
const std::string& description,
const CacheTestParam* param)
: CacheTest (testContext, name, description, param)
{ }
virtual ~ComputeCacheTest (void) { }
virtual void initPrograms (SourceCollections& programCollection) const;
virtual TestInstance* createInstance (Context& context) const;
};
class ComputeCacheTestInstance : public CacheTestInstance
{
public:
ComputeCacheTestInstance (Context& context,
const CacheTestParam* param);
virtual ~ComputeCacheTestInstance (void);
protected:
virtual tcu::TestStatus verifyTestResult (void);
void buildDescriptorSets (deUint32 ndx);
void buildShader (deUint32 ndx);
void buildPipeline (const CacheTestParam* param, deUint32 ndx);
protected:
Move<VkBuffer> m_inputBuf;
de::MovePtr<Allocation> m_inputBufferAlloc;
Move<VkShaderModule> m_computeShaderModule[PIPELINE_CACHE_NDX_COUNT];
Move<VkBuffer> m_outputBuf[PIPELINE_CACHE_NDX_COUNT];
de::MovePtr<Allocation> m_outputBufferAlloc[PIPELINE_CACHE_NDX_COUNT];
Move<VkDescriptorPool> m_descriptorPool[PIPELINE_CACHE_NDX_COUNT];
Move<VkDescriptorSetLayout> m_descriptorSetLayout[PIPELINE_CACHE_NDX_COUNT];
Move<VkDescriptorSet> m_descriptorSet[PIPELINE_CACHE_NDX_COUNT];
Move<VkPipelineLayout> m_pipelineLayout[PIPELINE_CACHE_NDX_COUNT];
VkPipeline m_pipeline[PIPELINE_CACHE_NDX_COUNT];
VkPipelineCreationFeedbackEXT m_pipelineCreationFeedback[PIPELINE_CACHE_NDX_COUNT];
VkPipelineCreationFeedbackEXT m_pipelineStageCreationFeedback[PIPELINE_CACHE_NDX_COUNT];
};
void ComputeCacheTest::initPrograms (SourceCollections& programCollection) const
{
programCollection.glslSources.add("basic_compute_1") << glu::ComputeSource(
"#version 310 es\n"
"layout(local_size_x = 1) in;\n"
"layout(std430) buffer;\n"
"layout(binding = 0) readonly buffer Input0\n"
"{\n"
" vec4 elements[];\n"
"} input_data0;\n"
"layout(binding = 1) writeonly buffer Output\n"
"{\n"
" vec4 elements[];\n"
"} output_data;\n"
"void main()\n"
"{\n"
" uint ident = gl_GlobalInvocationID.x;\n"
" output_data.elements[ident] = input_data0.elements[ident] * input_data0.elements[ident];\n"
"}");
programCollection.glslSources.add("basic_compute_2") << glu::ComputeSource(
"#version 310 es\n"
"layout(local_size_x = 1) in;\n"
"layout(std430) buffer;\n"
"layout(binding = 0) readonly buffer Input0\n"
"{\n"
" vec4 elements[];\n"
"} input_data0;\n"
"layout(binding = 1) writeonly buffer Output\n"
"{\n"
" vec4 elements[];\n"
"} output_data;\n"
"void main()\n"
"{\n"
" uint ident = gl_GlobalInvocationID.x;\n"
" output_data.elements[ident] = input_data0.elements[ident];\n"
"}");
}
TestInstance* ComputeCacheTest::createInstance (Context& context) const
{
return new ComputeCacheTestInstance(context, &m_param);
}
void ComputeCacheTestInstance::buildDescriptorSets (deUint32 ndx)
{
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice vkDevice = m_context.getDevice();
// Create descriptor set layout
DescriptorSetLayoutBuilder descLayoutBuilder;
for (deUint32 bindingNdx = 0u; bindingNdx < 2u; bindingNdx++)
descLayoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_COMPUTE_BIT);
m_descriptorSetLayout[ndx] = descLayoutBuilder.build(vk, vkDevice);
}
void ComputeCacheTestInstance::buildShader (deUint32 ndx)
{
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice vkDevice = m_context.getDevice();
std::string shader_name("basic_compute_");
shader_name += (ndx == PIPELINE_CACHE_NDX_DERIVATIVE) ? "2" : "1";
// Create compute shader
VkShaderModuleCreateInfo shaderModuleCreateInfo =
{
VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkShaderModuleCreateFlags flags;
m_context.getBinaryCollection().get(shader_name).getSize(), // deUintptr codeSize;
(deUint32*)m_context.getBinaryCollection().get(shader_name).getBinary(), // const deUint32* pCode;
};
m_computeShaderModule[ndx] = createShaderModule(vk, vkDevice, &shaderModuleCreateInfo);
}
void ComputeCacheTestInstance::buildPipeline (const CacheTestParam* param, deUint32 ndx)
{
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice vkDevice = m_context.getDevice();
deMemset(&m_pipelineCreationFeedback[ndx], 0, sizeof(VkPipelineCreationFeedbackEXT));
deMemset(&m_pipelineStageCreationFeedback[ndx], 0, sizeof(VkPipelineCreationFeedbackEXT));
const VkPipelineCreationFeedbackCreateInfoEXT pipelineCreationFeedbackCreateInfo =
{
VK_STRUCTURE_TYPE_PIPELINE_CREATION_FEEDBACK_CREATE_INFO_EXT, // VkStructureType sType;
DE_NULL, // const void * pNext;
&m_pipelineCreationFeedback[ndx], // VkPipelineCreationFeedbackEXT* pPipelineCreationFeedback;
1u, // deUint32 pipelineStageCreationFeedbackCount;
&m_pipelineStageCreationFeedback[ndx] // VkPipelineCreationFeedbackEXT* pPipelineStageCreationFeedbacks;
};
// Create compute pipeline layout
const VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo =
{
VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkPipelineLayoutCreateFlags flags;
1u, // deUint32 setLayoutCount;
&m_descriptorSetLayout[ndx].get(), // const VkDescriptorSetLayout* pSetLayouts;
0u, // deUint32 pushConstantRangeCount;
DE_NULL, // const VkPushConstantRange* pPushConstantRanges;
};
m_pipelineLayout[ndx] = createPipelineLayout(vk, vkDevice, &pipelineLayoutCreateInfo);
const VkPipelineShaderStageCreateInfo stageCreateInfo =
{
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkPipelineShaderStageCreateFlags flags;
VK_SHADER_STAGE_COMPUTE_BIT, // VkShaderStageFlagBits stage;
*m_computeShaderModule[ndx], // VkShaderModule module;
"main", // const char* pName;
DE_NULL, // const VkSpecializationInfo* pSpecializationInfo;
};
VkComputePipelineCreateInfo pipelineCreateInfo =
{
VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, // VkStructureType sType;
&pipelineCreationFeedbackCreateInfo, // const void* pNext;
0u, // VkPipelineCreateFlags flags;
stageCreateInfo, // VkPipelineShaderStageCreateInfo stage;
*m_pipelineLayout[ndx], // VkPipelineLayout layout;
(VkPipeline)0, // VkPipeline basePipelineHandle;
0u, // deInt32 basePipelineIndex;
};
if (ndx != PIPELINE_CACHE_NDX_DERIVATIVE)
{
pipelineCreateInfo.flags = VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT;
}
if (ndx == PIPELINE_CACHE_NDX_DERIVATIVE)
{
pipelineCreateInfo.flags = VK_PIPELINE_CREATE_DERIVATIVE_BIT;
pipelineCreateInfo.basePipelineHandle = m_pipeline[PIPELINE_CACHE_NDX_NO_CACHE];
pipelineCreateInfo.basePipelineIndex = -1;
}
if (ndx == PIPELINE_CACHE_NDX_CACHED && !param->isDelayedDestroy())
{
// Destroy the NO_CACHE pipeline to check that the cached one really hits cache,
// except for the case where we're testing cache hit of a pipeline still active.
vk.destroyPipeline(vkDevice, m_pipeline[PIPELINE_CACHE_NDX_NO_CACHE], DE_NULL);
}
vk.createComputePipelines(vkDevice, *m_cache, 1u, &pipelineCreateInfo, DE_NULL, &m_pipeline[ndx]);
if (ndx != PIPELINE_CACHE_NDX_NO_CACHE)
{
// Destroy the pipeline as soon as it is created, except the NO_CACHE because
// it is needed as a base pipeline for the derivative case.
vk.destroyPipeline(vkDevice, m_pipeline[ndx], DE_NULL);
if (ndx == PIPELINE_CACHE_NDX_CACHED && param->isDelayedDestroy())
{
// Destroy the pipeline we didn't destroy earlier for the isDelayedDestroy case.
vk.destroyPipeline(vkDevice, m_pipeline[PIPELINE_CACHE_NDX_NO_CACHE], DE_NULL);
}
}
}
ComputeCacheTestInstance::ComputeCacheTestInstance (Context& context,
const CacheTestParam* param)
: CacheTestInstance (context, param)
{
for (deUint32 ndx = 0; ndx < PIPELINE_CACHE_NDX_COUNT; ndx++)
{
buildDescriptorSets(ndx);
buildShader(ndx);
buildPipeline(param, ndx);
}
}
ComputeCacheTestInstance::~ComputeCacheTestInstance (void)
{
}
tcu::TestStatus ComputeCacheTestInstance::verifyTestResult (void)
{
tcu::TestLog &log = m_context.getTestContext().getLog();
deBool durationZeroWarning = DE_FALSE;
deBool cachedPipelineWarning = DE_FALSE;
for (deUint32 ndx = 0; ndx < PIPELINE_CACHE_NDX_COUNT; ndx++)
{
std::ostringstream message;
message << getCaseStr(ndx);
// No need to check per stage status as it is compute pipeline (only one stage) and Vulkan spec mentions that:
// "One common scenario for an implementation to skip per-stage feedback is when
// VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT_EXT is set in pPipelineCreationFeedback."
//
// Check first that the no cached pipeline was missed in the pipeline cache
if (!(m_pipelineCreationFeedback[ndx].flags & VK_PIPELINE_CREATION_FEEDBACK_VALID_BIT_EXT))
{
message << ": invalid data";
return tcu::TestStatus::fail(message.str());
}
if (m_param->isCacheDisabled() && m_pipelineCreationFeedback[ndx].flags & VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT_EXT)
{
message << ": feedback indicates pipeline hit cache when it shouldn't";
return tcu::TestStatus::fail(message.str());
}
if (ndx == PIPELINE_CACHE_NDX_NO_CACHE && m_pipelineCreationFeedback[ndx].flags & VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT_EXT)
{
message << ": hit the cache when it shouldn't";
return tcu::TestStatus::fail(message.str());
}
if (!(ndx == PIPELINE_CACHE_NDX_DERIVATIVE && !m_param->isCacheDisabled()) && m_pipelineCreationFeedback[ndx].flags & VK_PIPELINE_CREATION_FEEDBACK_BASE_PIPELINE_ACCELERATION_BIT_EXT)
{
message << ": feedback indicates base pipeline acceleration when it shouldn't";
return tcu::TestStatus::fail(message.str());
}
if (ndx == PIPELINE_CACHE_NDX_CACHED && !m_param->isCacheDisabled() && (m_pipelineCreationFeedback[ndx].flags & VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT_EXT) == 0)
{
message << "\nWarning: Cached pipeline case did not hit the cache";
cachedPipelineWarning = DE_TRUE;
}
if (m_pipelineCreationFeedback[ndx].duration == 0)
{
message << "\nWarning: Pipeline creation feedback reports duration spent creating a pipeline was zero nanoseconds\n";
durationZeroWarning = DE_TRUE;
}
message << "\n";
message << "\t\t Hit cache ? \t\t\t" << (m_pipelineCreationFeedback[ndx].flags & VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT_EXT ? "yes" : "no") << "\n";
message << "\t\t Base Pipeline Acceleration ? \t" << (m_pipelineCreationFeedback[ndx].flags & VK_PIPELINE_CREATION_FEEDBACK_BASE_PIPELINE_ACCELERATION_BIT_EXT ? "yes" : "no") << "\n";
message << "\t\t Duration (ns): \t\t" << m_pipelineCreationFeedback[ndx].duration << "\n";
message << "\t Compute Stage\n";
// According to the spec:
//
// "An implementation should write pipeline creation feedback to pPipelineCreationFeedback and
// may write pipeline stage creation feedback to pPipelineStageCreationFeedbacks."
if (!(m_pipelineStageCreationFeedback[ndx].flags & VK_PIPELINE_CREATION_FEEDBACK_VALID_BIT_EXT))
{
// According to the spec:
// "If the VK_PIPELINE_CREATION_FEEDBACK_VALID_BIT_EXT is not set in flags, an implementation
// must not set any other bits in flags, and all other VkPipelineCreationFeedbackEXT data members are undefined."
if (m_pipelineStageCreationFeedback[ndx].flags)
{
std::ostringstream errorMsg;
errorMsg << getCaseStr(ndx) << ": Creation feedback is not valid for compute stage but there are other flags written";
return tcu::TestStatus::fail(errorMsg.str());
}
message << "\t\t Pipeline Creation Feedback data is not valid\n";
}
else
{
if (m_param->isCacheDisabled() && m_pipelineStageCreationFeedback[ndx].flags & VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT_EXT)
{
std::ostringstream errorMsg;
errorMsg << getCaseStr(ndx) << ": feedback indicates pipeline compute stage hit cache when it shouldn't";
return tcu::TestStatus::fail(errorMsg.str());
}
if (ndx == PIPELINE_CACHE_NDX_CACHED && !m_param->isCacheDisabled() && (m_pipelineStageCreationFeedback[ndx].flags & VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT_EXT) == 0)
{
message << "Warning: pipeline stage did not hit the cache\n";
cachedPipelineWarning = DE_TRUE;
}
if (cachedPipelineWarning && m_pipelineStageCreationFeedback[ndx].flags & VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT_EXT)
{
// We only set the warning when the pipeline nor the pipeline stages hit the cache. If any of them did, them disable the warning.
cachedPipelineWarning = DE_FALSE;
}
message << "\t\t Hit cache ? \t\t\t" << (m_pipelineStageCreationFeedback[ndx].flags & VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT_EXT ? "yes" : "no") << "\n";
message << "\t\t Base Pipeline Acceleration ? \t" << (m_pipelineStageCreationFeedback[ndx].flags & VK_PIPELINE_CREATION_FEEDBACK_BASE_PIPELINE_ACCELERATION_BIT_EXT ? "yes" : "no") << "\n";
message << "\t\t Duration (ns): \t\t" << m_pipelineStageCreationFeedback[ndx].duration << "\n";
}
log << tcu::TestLog::Message << message.str() << tcu::TestLog::EndMessage;
}
if (cachedPipelineWarning)
{
return tcu::TestStatus(QP_TEST_RESULT_QUALITY_WARNING, "Cached pipeline or stage did not hit the cache");
}
if (durationZeroWarning)
{
return tcu::TestStatus(QP_TEST_RESULT_QUALITY_WARNING, "Pipeline creation feedback reports duration spent creating a pipeline was zero nanoseconds");
}
return tcu::TestStatus::pass("Pass");
}
} // anonymous
tcu::TestCaseGroup* createCreationFeedbackTests (tcu::TestContext& testCtx, PipelineConstructionType pipelineConstructionType)
{
de::MovePtr<tcu::TestCaseGroup> cacheTests (new tcu::TestCaseGroup(testCtx, "creation_feedback", "pipeline creation feedback tests"));
// Graphics Pipeline Tests
{
de::MovePtr<tcu::TestCaseGroup> graphicsTests (new tcu::TestCaseGroup(testCtx, "graphics_tests", "Test pipeline creation feedback with graphics pipeline."));
const VkShaderStageFlags vertFragStages = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
const VkShaderStageFlags vertGeomFragStages = vertFragStages | VK_SHADER_STAGE_GEOMETRY_BIT;
const VkShaderStageFlags vertTessFragStages = vertFragStages | VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT | VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
const std::vector<CacheTestParam> testParams
{
{ pipelineConstructionType, vertFragStages, DE_FALSE, DE_FALSE },
{ pipelineConstructionType, vertGeomFragStages, DE_FALSE, DE_FALSE },
{ pipelineConstructionType, vertTessFragStages, DE_FALSE, DE_FALSE },
{ pipelineConstructionType, vertFragStages, DE_TRUE, DE_FALSE },
{ pipelineConstructionType, vertGeomFragStages, DE_TRUE, DE_FALSE },
{ pipelineConstructionType, vertTessFragStages, DE_TRUE, DE_FALSE },
{ pipelineConstructionType, vertFragStages, DE_FALSE, DE_TRUE },
{ pipelineConstructionType, vertGeomFragStages, DE_FALSE, DE_TRUE },
{ pipelineConstructionType, vertTessFragStages, DE_FALSE, DE_TRUE },
};
for (auto& param : testParams)
graphicsTests->addChild(newTestCase<GraphicsCacheTest>(testCtx, &param));
cacheTests->addChild(graphicsTests.release());
}
// Compute Pipeline Tests - don't repeat those tests for graphics pipeline library
if (pipelineConstructionType == PIPELINE_CONSTRUCTION_TYPE_MONOLITHIC)
{
de::MovePtr<tcu::TestCaseGroup> computeTests (new tcu::TestCaseGroup(testCtx, "compute_tests", "Test pipeline creation feedback with compute pipeline."));
const std::vector<CacheTestParam> testParams
{
{ pipelineConstructionType, VK_SHADER_STAGE_COMPUTE_BIT, DE_FALSE, DE_FALSE },
{ pipelineConstructionType, VK_SHADER_STAGE_COMPUTE_BIT, DE_TRUE, DE_FALSE },
{ pipelineConstructionType, VK_SHADER_STAGE_COMPUTE_BIT, DE_FALSE, DE_TRUE },
};
for (auto& param : testParams)
computeTests->addChild(newTestCase<ComputeCacheTest>(testCtx, &param));
cacheTests->addChild(computeTests.release());
}
return cacheTests.release();
}
} // pipeline
} // vkt