| /*------------------------------------------------------------------------ |
| * Vulkan Conformance Tests |
| * ------------------------ |
| * |
| * Copyright (c) 2020 The Khronos Group Inc. |
| * Copyright (c) 2020 Advanced Micro Devices, Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| /*! |
| * \file |
| * \brief Pipeline Cache Tests |
| */ |
| /*--------------------------------------------------------------------*/ |
| |
| #include "vktPipelineCreationCacheControlTests.hpp" |
| |
| #include "deRandom.hpp" |
| #include "deUniquePtr.hpp" |
| #include "tcuStringTemplate.hpp" |
| #include "vkDeviceUtil.hpp" |
| #include "vkRefUtil.hpp" |
| #include "vktConstexprVectorUtil.hpp" |
| #include "vktTestCase.hpp" |
| #include "vktTestCaseUtil.hpp" |
| |
| #include <chrono> |
| #include <random> |
| #include <string> |
| #include <vector> |
| |
| namespace vkt |
| { |
| namespace pipeline |
| { |
| namespace |
| { |
| using namespace vk; |
| |
| using tcu::StringTemplate; |
| using tcu::TestCaseGroup; |
| using tcu::TestContext; |
| using tcu::TestStatus; |
| |
| using ::std::array; |
| using ::std::string; |
| using ::std::vector; |
| |
| /*--------------------------------------------------------------------*//*! |
| * Elements common to all test types |
| *//*--------------------------------------------------------------------*/ |
| namespace test_common |
| { |
| static constexpr auto VK_NULL_HANDLE = DE_NULL; |
| |
| using ::std::chrono::high_resolution_clock; |
| using ::std::chrono::microseconds; |
| |
| using duration = high_resolution_clock::duration; |
| using UniquePipeline = Move<VkPipeline>; |
| using UniqueShaderModule = Move<VkShaderModule>; |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Paired Vulkan API result with elapsed duration |
| *//*--------------------------------------------------------------------*/ |
| struct TimedResult |
| { |
| VkResult result; |
| duration elapsed; |
| }; |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Validation function type output from vkCreate*Pipelines() |
| * |
| * \param result - VkResult returned from API call |
| * \param pipeliens - vector of pipelines created |
| * \param elapsed - high_resolution_clock::duration of time elapsed in API |
| * \param reason - output string to give the reason for failure |
| * |
| * \return QP_TEST_RESULT_PASS on success QP_TEST_RESULT_FAIL otherwise |
| *//*--------------------------------------------------------------------*/ |
| using Validator = qpTestResult (*)(VkResult, const vector<UniquePipeline>&, duration, string&); |
| |
| static constexpr size_t VALIDATOR_ARRAY_MAX = 4; |
| using ValidatorArray = ConstexprVector<Validator, VALIDATOR_ARRAY_MAX>; |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Run a loop of validation tests and return the result |
| *//*--------------------------------------------------------------------*/ |
| template <typename pipelines_t, qpTestResult FAIL_RESULT = QP_TEST_RESULT_FAIL> |
| TestStatus validateResults(VkResult result, |
| const pipelines_t& pipelines, |
| duration elapsed, |
| const ValidatorArray& validators) |
| { |
| using de::contains; |
| static constexpr VkResult ALLOWED_RESULTS[] = {VK_SUCCESS, VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT}; |
| |
| string reason; |
| |
| if (contains(DE_ARRAY_BEGIN(ALLOWED_RESULTS), DE_ARRAY_END(ALLOWED_RESULTS), result) == DE_FALSE) |
| { |
| static const StringTemplate ERROR_MSG = {"Pipeline creation returned an error result: ${0}"}; |
| TCU_THROW(InternalError, ERROR_MSG.format(result).c_str()); |
| } |
| |
| for (const auto& validator : validators) |
| { |
| const auto qpResult = validator(result, pipelines, elapsed, reason); |
| if (qpResult != QP_TEST_RESULT_PASS) |
| { |
| return {qpResult, reason}; |
| } |
| } |
| |
| return TestStatus::pass("Test passed."); |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Generate an error if result does not match VK_RESULT |
| *//*--------------------------------------------------------------------*/ |
| template <VkResult VK_RESULT, qpTestResult FAIL_RESULT = QP_TEST_RESULT_FAIL> |
| qpTestResult checkResult(VkResult result, const vector<UniquePipeline>&, duration, string& reason) |
| { |
| if (VK_RESULT != result) |
| { |
| static const StringTemplate ERROR_MSG = {"Got ${0}, Expected ${1}"}; |
| reason = ERROR_MSG.format(result, VK_RESULT); |
| return FAIL_RESULT; |
| } |
| |
| return QP_TEST_RESULT_PASS; |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Generate an error if pipeline[INDEX] is not valid |
| *//*--------------------------------------------------------------------*/ |
| template <size_t INDEX, qpTestResult FAIL_RESULT = QP_TEST_RESULT_FAIL> |
| qpTestResult checkPipelineMustBeValid(VkResult, const vector<UniquePipeline>& pipelines, duration, string& reason) |
| { |
| if (pipelines.size() <= INDEX) |
| { |
| static const StringTemplate ERROR_MSG = {"Index ${0} is not in created pipelines (pipelines.size(): ${1})"}; |
| TCU_THROW(TestError, ERROR_MSG.format(INDEX, pipelines.size())); |
| } |
| |
| if (*pipelines[INDEX] == VK_NULL_HANDLE) |
| { |
| static const StringTemplate ERROR_MSG = {"pipelines[${0}] is not a valid VkPipeline object"}; |
| reason = ERROR_MSG.format(INDEX); |
| return FAIL_RESULT; |
| } |
| |
| return QP_TEST_RESULT_PASS; |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Generate an error if pipeline[INDEX] is not VK_NULL_HANDLE |
| *//*--------------------------------------------------------------------*/ |
| template <size_t INDEX, qpTestResult FAIL_RESULT = QP_TEST_RESULT_FAIL> |
| qpTestResult checkPipelineMustBeNull(VkResult, const vector<UniquePipeline>& pipelines, duration, string& reason) |
| { |
| if (pipelines.size() <= INDEX) |
| { |
| static const StringTemplate ERROR_MSG = {"Index ${0} is not in created pipelines (pipelines.size(): ${1})"}; |
| TCU_THROW(TestError, ERROR_MSG.format(INDEX, pipelines.size())); |
| } |
| |
| if (*pipelines[INDEX] != VK_NULL_HANDLE) |
| { |
| static const StringTemplate ERROR_MSG = {"pipelines[${0}] is not VK_NULL_HANDLE"}; |
| reason = ERROR_MSG.format(INDEX); |
| return FAIL_RESULT; |
| } |
| |
| return QP_TEST_RESULT_PASS; |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Generate an error if any pipeline is valid after an early-return failure |
| *//*--------------------------------------------------------------------*/ |
| template <size_t INDEX, qpTestResult FAIL_RESULT = QP_TEST_RESULT_FAIL> |
| qpTestResult checkPipelineNullAfterIndex(VkResult, const vector<UniquePipeline>& pipelines, duration, string& reason) |
| { |
| if (pipelines.size() <= INDEX) |
| { |
| static const StringTemplate ERROR_MSG = {"Index ${0} is not in created pipelines (pipelines.size(): ${1})"}; |
| TCU_THROW(TestError, ERROR_MSG.format(INDEX, pipelines.size())); |
| } |
| |
| if (pipelines.size() - 1 == INDEX) |
| { |
| static const StringTemplate ERROR_MSG = {"Index ${0} is the last pipeline, likely a malformed test case"}; |
| TCU_THROW(TestError, ERROR_MSG.format(INDEX)); |
| } |
| |
| // Only have to iterate through if the requested index is null |
| if (*pipelines[INDEX] == VK_NULL_HANDLE) |
| { |
| for (size_t i = INDEX + 1; i < pipelines.size(); ++i) |
| { |
| if (*pipelines[i] != VK_NULL_HANDLE) |
| { |
| static const StringTemplate ERROR_MSG = { |
| "pipelines[${0}] is not VK_NULL_HANDLE after a explicit early return index"}; |
| reason = ERROR_MSG.format(i); |
| return FAIL_RESULT; |
| } |
| } |
| } |
| |
| return QP_TEST_RESULT_PASS; |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * Time limit constants |
| *//*--------------------------------------------------------------------*/ |
| enum ElapsedTime |
| { |
| ELAPSED_TIME_INFINITE = microseconds{-1}.count(), |
| ELAPSED_TIME_IMMEDIATE = microseconds{500}.count(), |
| ELAPSED_TIME_FAST = microseconds{1000}.count() |
| }; |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Generate an error if elapsed time exceeds MAX_TIME |
| *//*--------------------------------------------------------------------*/ |
| template <ElapsedTime MAX_TIME, qpTestResult FAIL_RESULT = QP_TEST_RESULT_FAIL> |
| qpTestResult checkElapsedTime(VkResult, const vector<UniquePipeline>&, duration elapsed, string& reason) |
| { |
| #if defined(DE_DEBUG) |
| DE_UNREF(elapsed); |
| DE_UNREF(reason); |
| |
| // In debug mode timing is not likely to be accurate |
| return QP_TEST_RESULT_PASS; |
| #else |
| |
| using ::std::chrono::duration_cast; |
| |
| static constexpr microseconds ALLOWED_TIME = microseconds{MAX_TIME}; |
| |
| if (elapsed > ALLOWED_TIME) |
| { |
| static const StringTemplate ERROR_MSG = {"pipeline creation took longer than ${0}us (actual time: ${1}us)"}; |
| reason = ERROR_MSG.format(ALLOWED_TIME.count(), duration_cast<microseconds>(elapsed).count()); |
| return FAIL_RESULT; |
| } |
| |
| return QP_TEST_RESULT_PASS; |
| #endif |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Test case parameters |
| *//*--------------------------------------------------------------------*/ |
| struct TestParams |
| { |
| enum CacheType |
| { |
| NO_CACHE = 0, |
| EXPLICIT_CACHE, |
| DERIVATIVE_HANDLE, |
| DERIVATIVE_INDEX |
| }; |
| |
| struct Iteration |
| { |
| static constexpr size_t MAX_VARIANTS = 4; |
| using Variant = VkPipelineCreateFlags; |
| using VariantArray = ConstexprVector<Variant, MAX_VARIANTS>; |
| |
| static constexpr Variant NORMAL = 0; |
| static constexpr Variant NO_COMPILE = VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT_EXT; |
| static constexpr Variant EARLY_RETURN = NO_COMPILE | VK_PIPELINE_CREATE_EARLY_RETURN_ON_FAILURE_BIT_EXT; |
| |
| static constexpr VariantArray SINGLE_NORMAL = VariantArray{NORMAL}; |
| static constexpr VariantArray SINGLE_NOCOMPILE = VariantArray{NO_COMPILE}; |
| static constexpr VariantArray BATCH_NOCOMPILE_COMPILE_NOCOMPILE = VariantArray{NO_COMPILE, NORMAL, NO_COMPILE}; |
| static constexpr VariantArray BATCH_RETURN_COMPILE_NOCOMPILE = VariantArray{EARLY_RETURN, NORMAL, NO_COMPILE}; |
| |
| inline constexpr Iteration() : variants{}, validators{} {}; |
| inline constexpr Iteration(const VariantArray& v, const ValidatorArray& f) : variants{v}, validators{f} {}; |
| |
| VariantArray variants; |
| ValidatorArray validators; |
| }; |
| |
| static constexpr size_t MAX_ITERATIONS = 4; |
| using IterationArray = ConstexprVector<Iteration, MAX_ITERATIONS>; |
| |
| const char* name; |
| const char* description; |
| CacheType cacheType; |
| IterationArray iterations; |
| }; |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Verify extension and feature support |
| *//*--------------------------------------------------------------------*/ |
| void checkSupport(Context& context, const TestParams&) |
| { |
| static constexpr char EXT_NAME[] = "VK_EXT_pipeline_creation_cache_control"; |
| if (!context.requireDeviceFunctionality(EXT_NAME)) |
| { |
| TCU_THROW(NotSupportedError, "Extension 'VK_EXT_pipeline_creation_cache_control' is not supported"); |
| } |
| |
| const auto features = context.getPipelineCreationCacheControlFeaturesEXT(); |
| if (features.pipelineCreationCacheControl == DE_FALSE) |
| { |
| TCU_THROW(NotSupportedError, "Feature 'pipelineCreationCacheControl' is not enabled"); |
| } |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Generate a random floating point number as a string |
| *//*--------------------------------------------------------------------*/ |
| float randomFloat() |
| { |
| #if !defined(DE_DEBUG) |
| static de::Random state = {::std::random_device{}()}; |
| #else |
| static de::Random state = {0xDEADBEEF}; |
| #endif |
| |
| return state.getFloat(); |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Get a string of VkResults from a vector |
| *//*--------------------------------------------------------------------*/ |
| string getResultsString(const vector<VkResult>& results) |
| { |
| using ::std::ostringstream; |
| |
| ostringstream output; |
| |
| output << "results[" << results.size() << "]={ "; |
| |
| if (!results.empty()) |
| { |
| output << results[0]; |
| } |
| |
| for (size_t i = 1; i < results.size(); ++i) |
| { |
| output << ", " << results[i]; |
| } |
| |
| output << " }"; |
| |
| return output.str(); |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Cast a pointer to an the expected SPIRV type |
| *//*--------------------------------------------------------------------*/ |
| template <typename _t> |
| inline const deUint32* shader_cast(const _t* ptr) |
| { |
| return reinterpret_cast<const deUint32*>(ptr); |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Capture a container of Vulkan handles into Move<> types |
| *//*--------------------------------------------------------------------*/ |
| template <typename input_container_t, |
| typename handle_t = typename input_container_t::value_type, |
| typename move_t = Move<handle_t>, |
| typename deleter_t = Deleter<handle_t>, |
| typename output_t = vector<move_t>> |
| output_t wrapHandles(const DeviceInterface& vk, |
| VkDevice device, |
| const input_container_t& input, |
| const VkAllocationCallbacks* allocator = DE_NULL) |
| { |
| using ::std::begin; |
| using ::std::end; |
| using ::std::transform; |
| |
| auto output = output_t{}; |
| output.resize(input.size()); |
| |
| struct Predicate |
| { |
| deleter_t deleter; |
| move_t operator()(handle_t v) |
| { |
| return (v != VK_NULL_HANDLE) ? move_t{check(v), deleter} : move_t{}; |
| } |
| }; |
| |
| const auto wrapHandle = Predicate{deleter_t{vk, device, allocator}}; |
| |
| transform(begin(input), end(input), begin(output), wrapHandle); |
| |
| return output; |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief create vkPipelineCache for test params |
| *//*--------------------------------------------------------------------*/ |
| Move<VkPipelineCache> createPipelineCache(const DeviceInterface& vk, VkDevice device, const TestParams& params) |
| { |
| if (params.cacheType != TestParams::EXPLICIT_CACHE) |
| { |
| return {}; |
| } |
| |
| static constexpr auto cacheInfo = VkPipelineCacheCreateInfo{ |
| VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO, //sType |
| DE_NULL, //pNext |
| VkPipelineCacheCreateFlags{}, //flags |
| deUintptr{0}, //initialDataSize |
| DE_NULL //pInitialData |
| }; |
| |
| return createPipelineCache(vk, device, &cacheInfo); |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief create VkPipelineLayout with descriptor sets from test parameters |
| *//*--------------------------------------------------------------------*/ |
| Move<VkPipelineLayout> createPipelineLayout(const DeviceInterface& vk, |
| VkDevice device, |
| const vector<VkDescriptorSetLayout>& setLayouts, |
| const TestParams&) |
| { |
| const auto layoutCreateInfo = VkPipelineLayoutCreateInfo{ |
| VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // sType |
| DE_NULL, // pNext |
| VkPipelineLayoutCreateFlags{}, // flags |
| static_cast<deUint32>(setLayouts.size()), // setLayoutCount |
| setLayouts.data(), // pSetLayouts |
| deUint32{0u}, // pushConstantRangeCount |
| DE_NULL, // pPushConstantRanges |
| }; |
| |
| return createPipelineLayout(vk, device, &layoutCreateInfo); |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief create basic VkPipelineLayout from test parameters |
| *//*--------------------------------------------------------------------*/ |
| Move<VkPipelineLayout> createPipelineLayout(const DeviceInterface& vk, VkDevice device, const TestParams&) |
| { |
| static constexpr auto layoutCreateInfo = VkPipelineLayoutCreateInfo{ |
| VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // sType |
| DE_NULL, // pNext |
| VkPipelineLayoutCreateFlags{}, // flags |
| deUint32{0u}, // setLayoutCount |
| DE_NULL, // pSetLayouts |
| deUint32{0u}, // pushConstantRangeCount |
| DE_NULL, // pPushConstantRanges |
| }; |
| |
| return createPipelineLayout(vk, device, &layoutCreateInfo); |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Create array of shader modules |
| *//*--------------------------------------------------------------------*/ |
| vector<UniqueShaderModule> createShaderModules(const DeviceInterface& vk, |
| VkDevice device, |
| const BinaryCollection& collection, |
| const vector<const char*>& names) |
| { |
| auto output = vector<UniqueShaderModule>{}; |
| output.reserve(names.size()); |
| |
| for (const auto& name : names) |
| { |
| const auto& binary = collection.get(name); |
| const auto createInfo = VkShaderModuleCreateInfo{ |
| VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, // sType |
| DE_NULL, // pNext |
| VkShaderModuleCreateFlags{}, // flags |
| binary.getSize(), // codeSize |
| shader_cast(binary.getBinary()) // pCode |
| }; |
| |
| output.push_back(createShaderModule(vk, device, &createInfo)); |
| } |
| |
| return output; |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Create array of shader binding stages |
| *//*--------------------------------------------------------------------*/ |
| vector<VkPipelineShaderStageCreateInfo> createShaderStages(const vector<Move<VkShaderModule>>& modules, |
| const vector<VkShaderStageFlagBits>& stages) |
| { |
| DE_ASSERT(modules.size() == stages.size()); |
| |
| auto output = vector<VkPipelineShaderStageCreateInfo>{}; |
| output.reserve(modules.size()); |
| |
| int i = 0; |
| |
| for (const auto& module : modules) |
| { |
| const auto stageInfo = VkPipelineShaderStageCreateInfo{ |
| VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // sType |
| DE_NULL, // pNext |
| VkPipelineShaderStageCreateFlags{}, // flags |
| stages[i++], // stage |
| *module, // module |
| "main", // pName |
| DE_NULL // pSpecializationInfo |
| }; |
| |
| output.push_back(stageInfo); |
| } |
| |
| return output; |
| } |
| |
| } // namespace test_common |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Graphics pipeline specific testing |
| *//*--------------------------------------------------------------------*/ |
| namespace graphics_tests |
| { |
| using namespace test_common; |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Common graphics pipeline create info initialization |
| *//*--------------------------------------------------------------------*/ |
| VkGraphicsPipelineCreateInfo getPipelineCreateInfoCommon() |
| { |
| static constexpr auto VERTEX_BINDING = VkVertexInputBindingDescription{ |
| deUint32{0u}, // binding |
| sizeof(float[4]), // stride |
| VK_VERTEX_INPUT_RATE_VERTEX // inputRate |
| }; |
| |
| static constexpr auto VERTEX_ATTRIBUTE = VkVertexInputAttributeDescription{ |
| deUint32{0u}, // location |
| deUint32{0u}, // binding |
| VK_FORMAT_R32G32B32A32_SFLOAT, // format |
| deUint32{0u} // offset |
| }; |
| |
| static constexpr auto VERTEX_INPUT_STATE = VkPipelineVertexInputStateCreateInfo{ |
| VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, // sType |
| DE_NULL, // pNext |
| VkPipelineVertexInputStateCreateFlags{}, // flags |
| deUint32{1u}, // vertexBindingDescriptionCount |
| &VERTEX_BINDING, // pVertexBindingDescriptions |
| deUint32{1u}, // vertexAttributeDescriptionCount |
| &VERTEX_ATTRIBUTE // pVertexAttributeDescriptions |
| }; |
| |
| static constexpr auto IA_STATE = VkPipelineInputAssemblyStateCreateInfo{ |
| VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, // sType |
| DE_NULL, // pNext |
| VkPipelineInputAssemblyStateCreateFlags{}, // flags |
| VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, // topology |
| VK_TRUE // primitiveRestartEnable |
| }; |
| |
| static constexpr auto TESSALATION_STATE = VkPipelineTessellationStateCreateInfo{ |
| VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO, // sType |
| DE_NULL, // pNext |
| VkPipelineTessellationStateCreateFlags{}, // flags |
| deUint32{0u} // patchControlPoints |
| }; |
| |
| static constexpr auto VIEWPORT = VkViewport{ |
| 0.f, // x |
| 0.f, // y |
| 1.f, // width |
| 1.f, // height |
| 0.f, // minDepth |
| 1.f // maxDept |
| }; |
| |
| static constexpr auto SCISSOR_RECT = VkRect2D{ |
| {0, 0}, // offset |
| {256, 256} // extent |
| }; |
| |
| static constexpr auto VIEWPORT_STATE = VkPipelineViewportStateCreateInfo{ |
| VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, // sType |
| DE_NULL, // pNext |
| VkPipelineViewportStateCreateFlags{}, // flags |
| deUint32{1u}, // viewportCount |
| &VIEWPORT, // pViewports |
| deUint32{1u}, // scissorCount |
| &SCISSOR_RECT // pScissors |
| }; |
| |
| static constexpr auto RASTERIZATION_STATE = VkPipelineRasterizationStateCreateInfo{ |
| VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, // sType |
| DE_NULL, // pNext |
| VkPipelineRasterizationStateCreateFlags{}, // flags |
| VK_TRUE, // depthClampEnable |
| VK_TRUE, // rasterizerDiscardEnable |
| VK_POLYGON_MODE_FILL, // polygonMode |
| VK_CULL_MODE_NONE, // cullMode |
| VK_FRONT_FACE_CLOCKWISE, // frontFace |
| VK_FALSE, // depthBiasEnable |
| 0.f, // depthBiasConstantFactor |
| 0.f, // depthBiasClamp |
| 0.f, // depthBiasSlopeFactor |
| 1.f // lineWidth |
| }; |
| |
| static constexpr auto SAMPLE_MASK = VkSampleMask{}; |
| |
| static constexpr auto MULTISAMPLE_STATE = VkPipelineMultisampleStateCreateInfo{ |
| VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, // sType |
| DE_NULL, // pNext |
| VkPipelineMultisampleStateCreateFlags{}, // flags |
| VK_SAMPLE_COUNT_1_BIT, // rasterizationSamples |
| VK_FALSE, // sampleShadingEnable |
| 0.f, // minSampleShading |
| &SAMPLE_MASK, // pSampleMask |
| VK_FALSE, // alphaToCoverageEnable |
| VK_FALSE // alphaToOneEnable |
| }; |
| |
| static constexpr auto STENCIL_OP_STATE = VkStencilOpState{ |
| VK_STENCIL_OP_ZERO, // failOp |
| VK_STENCIL_OP_ZERO, // passOp |
| VK_STENCIL_OP_ZERO, // depthFailOp |
| VK_COMPARE_OP_ALWAYS, // compareOp |
| deUint32{0u}, // compareMask |
| deUint32{0u}, // writeMask |
| deUint32{0u} // reference |
| }; |
| |
| static constexpr auto DEPTH_STENCIL_STATE = VkPipelineDepthStencilStateCreateInfo{ |
| VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, // sType |
| DE_NULL, // pNext |
| VkPipelineDepthStencilStateCreateFlags{}, // flags |
| VK_FALSE, // depthTestEnable |
| VK_FALSE, // depthWriteEnable |
| VK_COMPARE_OP_ALWAYS, // depthCompareOp |
| VK_FALSE, // depthBoundsTestEnable |
| VK_FALSE, // stencilTestEnable |
| STENCIL_OP_STATE, // front |
| STENCIL_OP_STATE, // back |
| 0.f, // minDepthBounds |
| 1.f // maxDepthBounds |
| }; |
| |
| static constexpr auto COLOR_FLAGS_ALL = VkColorComponentFlags{VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | |
| VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT}; |
| |
| static constexpr auto COLOR_BLEND_ATTACH_STATE = VkPipelineColorBlendAttachmentState{ |
| VK_FALSE, // blendEnable |
| VK_BLEND_FACTOR_ONE, // srcColorBlendFactor |
| VK_BLEND_FACTOR_ZERO, // dstColorBlendFactor |
| VK_BLEND_OP_ADD, // colorBlendOp |
| VK_BLEND_FACTOR_ONE, // srcAlphaBlendFactor |
| VK_BLEND_FACTOR_ZERO, // dstAlphaBlendFactor |
| VK_BLEND_OP_ADD, // alphaBlendOp |
| COLOR_FLAGS_ALL // colorWriteMask |
| }; |
| |
| static constexpr auto COLOR_BLEND_STATE = VkPipelineColorBlendStateCreateInfo{ |
| VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, // sType |
| DE_NULL, // pNext |
| VkPipelineColorBlendStateCreateFlags{}, // flags |
| VK_FALSE, // logicOpEnable |
| VK_LOGIC_OP_SET, // logicOp |
| deUint32{1u}, // attachmentCount |
| &COLOR_BLEND_ATTACH_STATE, // pAttachments |
| {0.f, 0.f, 0.f, 0.f} // blendConstants[4] |
| }; |
| |
| static constexpr auto DYNAMIC_STATE = VkPipelineDynamicStateCreateInfo{ |
| VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, // sType; |
| DE_NULL, // pNext; |
| VkPipelineDynamicStateCreateFlags{}, // flags; |
| deUint32{0u}, // dynamicStateCount; |
| DE_NULL // pDynamicStates; |
| }; |
| |
| return VkGraphicsPipelineCreateInfo{ |
| VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, // sType |
| DE_NULL, // pNext |
| VkPipelineCreateFlags{}, // flags |
| deUint32{0u}, // stageCount |
| DE_NULL, // pStages |
| &VERTEX_INPUT_STATE, // pVertexInputState |
| &IA_STATE, // pInputAssemblyState |
| &TESSALATION_STATE, // pTessellationState |
| &VIEWPORT_STATE, // pViewportState |
| &RASTERIZATION_STATE, // pRasterizationState |
| &MULTISAMPLE_STATE, // pMultisampleState |
| &DEPTH_STENCIL_STATE, // pDepthStencilState |
| &COLOR_BLEND_STATE, // pColorBlendState |
| &DYNAMIC_STATE, // pDynamicState |
| VK_NULL_HANDLE, // layout |
| VK_NULL_HANDLE, // renderPass |
| deUint32{0u}, // subpass |
| VK_NULL_HANDLE, // basePipelineHandle |
| deInt32{-1} // basePipelineIndex |
| }; |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief create VkGraphicsPipelineCreateInfo structs from test iteration |
| *//*--------------------------------------------------------------------*/ |
| vector<VkGraphicsPipelineCreateInfo> createPipelineCreateInfos(const TestParams::Iteration& iteration, |
| const VkGraphicsPipelineCreateInfo& base, |
| VkPipeline basePipeline, |
| const TestParams& testParameter) |
| { |
| auto output = vector<VkGraphicsPipelineCreateInfo>{}; |
| output.reserve(iteration.variants.size()); |
| |
| deInt32 count = 0; |
| deInt32 basePipelineIndex = -1; |
| |
| for (VkPipelineCreateFlags flags : iteration.variants) |
| { |
| const auto curIndex = count++; |
| auto createInfo = base; |
| |
| if (testParameter.cacheType == TestParams::DERIVATIVE_INDEX) |
| { |
| if (flags & VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT_EXT) |
| { |
| if (basePipelineIndex != -1) |
| { |
| flags |= VK_PIPELINE_CREATE_DERIVATIVE_BIT; |
| } |
| } |
| else |
| { |
| flags |= VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT; |
| |
| if (basePipelineIndex == -1) |
| { |
| basePipelineIndex = curIndex; |
| } |
| } |
| } |
| |
| createInfo.flags = flags; |
| createInfo.basePipelineHandle = basePipeline; |
| createInfo.basePipelineIndex = basePipelineIndex; |
| |
| output.push_back(createInfo); |
| } |
| |
| return output; |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief create VkRenderPass object for Graphics test |
| *//*--------------------------------------------------------------------*/ |
| Move<VkRenderPass> createRenderPass(const DeviceInterface& vk, VkDevice device, const TestParams&) |
| { |
| static constexpr auto COLOR_FORMAT = VK_FORMAT_R8G8B8A8_UNORM; |
| |
| static constexpr auto COLOR_ATTACHMENT_REF = VkAttachmentReference{ |
| deUint32{0u}, // attachment |
| VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL // layout |
| }; |
| |
| static constexpr auto SUBPASS = VkSubpassDescription{ |
| VkSubpassDescriptionFlags{}, // flags |
| VK_PIPELINE_BIND_POINT_GRAPHICS, // pipelineBindPoint |
| deUint32{0u}, // inputAttachmentCount |
| DE_NULL, // pInputAttachments |
| deUint32{1u}, // colorAttachmentCount |
| &COLOR_ATTACHMENT_REF, // pColorAttachments |
| DE_NULL, // pResolveAttachments |
| DE_NULL, // pDepthStencilAttachment |
| deUint32{0u}, // preserveAttachmentCount |
| DE_NULL // pPreserveAttachments |
| }; |
| |
| static constexpr auto COLOR_ATTACHMENT = VkAttachmentDescription{ |
| VkAttachmentDescriptionFlags{}, // flags |
| COLOR_FORMAT, // format |
| VK_SAMPLE_COUNT_1_BIT, // samples |
| VK_ATTACHMENT_LOAD_OP_CLEAR, // loadOp |
| VK_ATTACHMENT_STORE_OP_STORE, // storeOp |
| VK_ATTACHMENT_LOAD_OP_DONT_CARE, // stencilLoadOp |
| VK_ATTACHMENT_STORE_OP_DONT_CARE, // stencilStoreOp |
| VK_IMAGE_LAYOUT_UNDEFINED, // initialLayout |
| VK_IMAGE_LAYOUT_PRESENT_SRC_KHR // finalLayout |
| }; |
| |
| static constexpr auto RENDER_PASS_CREATE_INFO = VkRenderPassCreateInfo{ |
| VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, // sType |
| DE_NULL, // pNext |
| VkRenderPassCreateFlags{}, // flags |
| deUint32{1u}, // attachmentCount |
| &COLOR_ATTACHMENT, // pAttachments |
| deUint32{1u}, // subpassCount |
| &SUBPASS, // pSubpasses |
| deUint32{0u}, // dependencyCount |
| DE_NULL // pDependencies |
| }; |
| |
| return createRenderPass(vk, device, &RENDER_PASS_CREATE_INFO); |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Initialize shader programs |
| *//*--------------------------------------------------------------------*/ |
| void initPrograms(SourceCollections& dst, const TestParams&) |
| { |
| using ::glu::FragmentSource; |
| using ::glu::VertexSource; |
| |
| // Vertex Shader |
| static const StringTemplate VS_TEXT = {"#version 310 es\n" |
| "layout(location = 0) in vec4 position;\n" |
| "layout(location = 0) out vec3 vertColor;\n" |
| "void main (void)\n" |
| "{\n" |
| " gl_Position = position;\n" |
| " vertColor = vec3(${0}, ${1}, ${2});\n" |
| "}\n"}; |
| |
| // Fragment Shader |
| static const StringTemplate FS_TEXT = {"#version 310 es\n" |
| "precision highp float;\n" |
| "layout(location = 0) in vec3 vertColor;\n" |
| "layout(location = 0) out vec4 outColor;\n" |
| "void main (void)\n" |
| "{\n" |
| " const vec3 fragColor = vec3(${0}, ${1}, ${2});\n" |
| " outColor = vec4((fragColor + vertColor) * 0.5, 1.0);\n" |
| "}\n"}; |
| |
| dst.glslSources.add("vertex") << VertexSource{VS_TEXT.format(randomFloat(), randomFloat(), randomFloat())}; |
| dst.glslSources.add("fragment") << FragmentSource{FS_TEXT.format(randomFloat(), randomFloat(), randomFloat())}; |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief return both result and elapsed time from pipeline creation |
| *//*--------------------------------------------------------------------*/ |
| template <typename create_infos_t, typename pipelines_t> |
| TimedResult timePipelineCreation(const DeviceInterface& vk, |
| const VkDevice device, |
| const VkPipelineCache cache, |
| const create_infos_t& createInfos, |
| pipelines_t& pipelines, |
| const VkAllocationCallbacks* pAllocator = DE_NULL) |
| { |
| DE_ASSERT(createInfos.size() <= pipelines.size()); |
| |
| const auto timeStart = high_resolution_clock::now(); |
| const auto result = vk.createGraphicsPipelines( |
| device, cache, static_cast<deUint32>(createInfos.size()), createInfos.data(), pAllocator, pipelines.data()); |
| const auto elapsed = high_resolution_clock::now() - timeStart; |
| return {result, elapsed}; |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Test instance function |
| *//*--------------------------------------------------------------------*/ |
| TestStatus testInstance(Context& context, const TestParams& testParameter) |
| { |
| const auto& vk = context.getDeviceInterface(); |
| const auto device = context.getDevice(); |
| const auto pipelineCache = createPipelineCache(vk, device, testParameter); |
| const auto layout = createPipelineLayout(vk, device, testParameter); |
| const auto renderPass = createRenderPass(vk, device, testParameter); |
| const auto modules = createShaderModules(vk, device, context.getBinaryCollection(), {"vertex", "fragment"}); |
| const auto shaderStages = createShaderStages(modules, {VK_SHADER_STAGE_VERTEX_BIT, VK_SHADER_STAGE_FRAGMENT_BIT}); |
| |
| // Placeholder for base pipeline if using cacheType == DERIVATIVE_HANDLE |
| auto basePipeline = UniquePipeline{}; |
| |
| auto baseCreateInfo = getPipelineCreateInfoCommon(); |
| baseCreateInfo.layout = layout.get(); |
| baseCreateInfo.renderPass = renderPass.get(); |
| baseCreateInfo.stageCount = static_cast<deUint32>(shaderStages.size()); |
| baseCreateInfo.pStages = shaderStages.data(); |
| |
| auto results = vector<VkResult>{}; |
| results.reserve(testParameter.iterations.size()); |
| |
| for (const auto& i : testParameter.iterations) |
| { |
| const auto createInfos = createPipelineCreateInfos(i, baseCreateInfo, basePipeline.get(), testParameter); |
| auto created = vector<VkPipeline>{}; |
| created.resize(createInfos.size()); |
| |
| const auto timedResult = timePipelineCreation(vk, device, pipelineCache.get(), createInfos, created); |
| auto pipelines = wrapHandles(vk, device, created); |
| |
| const auto status = validateResults(timedResult.result, pipelines, timedResult.elapsed, i.validators); |
| if (status.getCode() != QP_TEST_RESULT_PASS) |
| { |
| return status; |
| } |
| |
| if ((testParameter.cacheType == TestParams::DERIVATIVE_HANDLE) && (*basePipeline == VK_NULL_HANDLE)) |
| { |
| for (auto& pipeline : pipelines) |
| { |
| if (*pipeline != VK_NULL_HANDLE) |
| { |
| basePipeline = pipeline; |
| break; |
| } |
| } |
| } |
| |
| results.push_back(timedResult.result); |
| } |
| |
| static const StringTemplate PASS_MSG = {"Test Passed. ${0}"}; |
| return TestStatus::pass(PASS_MSG.format(getResultsString(results))); |
| } |
| |
| } // namespace graphics_tests |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Compute pipeline specific testing |
| *//*--------------------------------------------------------------------*/ |
| namespace compute_tests |
| { |
| using namespace test_common; |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief create VkComputePipelineCreateInfo structs from test iteration |
| *//*--------------------------------------------------------------------*/ |
| vector<VkComputePipelineCreateInfo> createPipelineCreateInfos(const TestParams::Iteration& iteration, |
| const VkComputePipelineCreateInfo& base, |
| VkPipeline basePipeline, |
| const TestParams& testParameter) |
| { |
| auto output = vector<VkComputePipelineCreateInfo>{}; |
| output.reserve(iteration.variants.size()); |
| |
| deInt32 count = 0; |
| deInt32 basePipelineIndex = -1; |
| |
| for (VkPipelineCreateFlags flags : iteration.variants) |
| { |
| const auto curIndex = count++; |
| auto createInfo = base; |
| |
| if (testParameter.cacheType == TestParams::DERIVATIVE_INDEX) |
| { |
| if (flags & VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT_EXT) |
| { |
| if (basePipelineIndex != -1) |
| { |
| flags |= VK_PIPELINE_CREATE_DERIVATIVE_BIT; |
| } |
| } |
| else |
| { |
| flags |= VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT; |
| |
| if (basePipelineIndex == -1) |
| { |
| basePipelineIndex = curIndex; |
| } |
| } |
| } |
| |
| createInfo.flags = flags; |
| createInfo.basePipelineHandle = basePipeline; |
| createInfo.basePipelineIndex = basePipelineIndex; |
| |
| output.push_back(createInfo); |
| } |
| |
| return output; |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief create compute descriptor set layout |
| *//*--------------------------------------------------------------------*/ |
| Move<VkDescriptorSetLayout> createDescriptorSetLayout(const DeviceInterface& vk, VkDevice device, const TestParams&) |
| { |
| static constexpr auto DESCRIPTOR_SET_LAYOUT_BINDING = VkDescriptorSetLayoutBinding{ |
| deUint32{0u}, // binding |
| VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, // descriptorType |
| deUint32{1u}, // descriptorCount |
| VK_SHADER_STAGE_COMPUTE_BIT, // stageFlags |
| DE_NULL // pImmutableSamplers |
| }; |
| |
| static constexpr auto DESCRIPTOR_SET_LAYOUT_CREATE_INFO = VkDescriptorSetLayoutCreateInfo{ |
| VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, // sType |
| DE_NULL, // pNext |
| VkDescriptorSetLayoutCreateFlags{}, // flags |
| deUint32{1u}, // bindingCount |
| &DESCRIPTOR_SET_LAYOUT_BINDING // pBindings |
| }; |
| |
| return createDescriptorSetLayout(vk, device, &DESCRIPTOR_SET_LAYOUT_CREATE_INFO); |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Initialize shader programs |
| *//*--------------------------------------------------------------------*/ |
| void initPrograms(SourceCollections& dst, const TestParams&) |
| { |
| using ::glu::ComputeSource; |
| |
| static const StringTemplate CS_TEXT = {"#version 450\n" |
| "precision highp float;\n" |
| "layout (local_size_x = 64, local_size_y = 1, local_size_z = 1) in;\n" |
| "layout (std140, binding = 0) buffer buf { vec3 data[]; };\n" |
| "void main (void)\n" |
| "{\n" |
| " data[gl_GlobalInvocationID.x] = vec3(${0}, ${1}, ${2});\n" |
| "}\n"}; |
| |
| dst.glslSources.add("compute") |
| << ComputeSource{CS_TEXT.format(randomFloat(), randomFloat(), randomFloat())}; |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief return both result and elapsed time from pipeline creation |
| *//*--------------------------------------------------------------------*/ |
| template <typename create_infos_t, typename pipelines_t> |
| TimedResult timePipelineCreation(const DeviceInterface& vk, |
| const VkDevice device, |
| const VkPipelineCache cache, |
| const create_infos_t& createInfos, |
| pipelines_t& pipelines, |
| const VkAllocationCallbacks* pAllocator = DE_NULL) |
| { |
| DE_ASSERT(createInfos.size() <= pipelines.size()); |
| |
| const auto timeStart = high_resolution_clock::now(); |
| const auto result = vk.createComputePipelines( |
| device, cache, static_cast<deUint32>(createInfos.size()), createInfos.data(), pAllocator, pipelines.data()); |
| const auto elapsed = high_resolution_clock::now() - timeStart; |
| return {result, elapsed}; |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Test instance function |
| *//*--------------------------------------------------------------------*/ |
| TestStatus testInstance(Context& context, const TestParams& testParameter) |
| { |
| const auto& vk = context.getDeviceInterface(); |
| const auto device = context.getDevice(); |
| const auto pipelineCache = createPipelineCache(vk, device, testParameter); |
| const auto descriptorSetLayout = createDescriptorSetLayout(vk, device, testParameter); |
| const auto pipelineLayout = createPipelineLayout(vk, device, {descriptorSetLayout.get()}, testParameter); |
| const auto modules = createShaderModules(vk, device, context.getBinaryCollection(), {"compute"}); |
| const auto shaderStages = createShaderStages(modules, {VK_SHADER_STAGE_COMPUTE_BIT}); |
| |
| // Placeholder for base pipeline if using cacheType == DERIVATIVE_HANDLE |
| auto basePipeline = UniquePipeline{}; |
| |
| const auto baseCreateInfo = VkComputePipelineCreateInfo{ |
| VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, // sType |
| DE_NULL, // pNext |
| VkPipelineCreateFlags{}, // flags |
| shaderStages[0], // stage |
| pipelineLayout.get(), // layout |
| VK_NULL_HANDLE, // basePipelineHandle |
| deInt32{-1} // basePipelineIndex |
| }; |
| |
| auto results = vector<VkResult>{}; |
| results.reserve(testParameter.iterations.size()); |
| |
| for (const auto& i : testParameter.iterations) |
| { |
| const auto createInfos = createPipelineCreateInfos(i, baseCreateInfo, basePipeline.get(), testParameter); |
| auto created = vector<VkPipeline>{}; |
| created.resize(createInfos.size()); |
| |
| const auto timedResult = timePipelineCreation(vk, device, pipelineCache.get(), createInfos, created); |
| auto pipelines = wrapHandles(vk, device, created); |
| |
| const auto status = validateResults(timedResult.result, pipelines, timedResult.elapsed, i.validators); |
| if (status.getCode() != QP_TEST_RESULT_PASS) |
| { |
| return status; |
| } |
| |
| if ((testParameter.cacheType == TestParams::DERIVATIVE_HANDLE) && (*basePipeline == VK_NULL_HANDLE)) |
| { |
| for (auto& pipeline : pipelines) |
| { |
| if (*pipeline != VK_NULL_HANDLE) |
| { |
| basePipeline = pipeline; |
| break; |
| } |
| } |
| } |
| |
| results.push_back(timedResult.result); |
| } |
| |
| static const StringTemplate PASS_MSG = {"Test Passed. ${0}"}; |
| return TestStatus::pass(PASS_MSG.format(getResultsString(results))); |
| } |
| |
| } // namespace compute_tests |
| |
| using namespace test_common; |
| |
| // Disable formatting on this next block for readability |
| // clang-format off |
| /*--------------------------------------------------------------------*//*! |
| * \brief Duplicate single pipeline recreation with explicit caching |
| *//*--------------------------------------------------------------------*/ |
| static constexpr TestParams DUPLICATE_SINGLE_RECREATE_EXPLICIT_CACHING = |
| { |
| "duplicate_single_recreate_explicit_caching", |
| "Duplicate single pipeline recreation with explicit caching", |
| TestParams::EXPLICIT_CACHE, |
| TestParams::IterationArray |
| { |
| TestParams::Iteration{ |
| // Iteration [0]: Force compilation of pipeline |
| TestParams::Iteration::SINGLE_NORMAL, |
| ValidatorArray{ |
| // Fail if result is not VK_SUCCESS |
| checkResult<VK_SUCCESS>, |
| // Fail if pipeline is not valid |
| checkPipelineMustBeValid<0> |
| } |
| }, |
| TestParams::Iteration{ |
| // Iteration [1]: Request compilation of same pipeline without compile |
| TestParams::Iteration::SINGLE_NOCOMPILE, |
| ValidatorArray{ |
| // Warn if result is not VK_SUCCESS |
| checkResult<VK_SUCCESS, QP_TEST_RESULT_COMPATIBILITY_WARNING>, |
| // Warn if pipeline is not valid |
| checkPipelineMustBeValid<0, QP_TEST_RESULT_COMPATIBILITY_WARNING>, |
| // Warn if pipeline took too long |
| checkElapsedTime<ELAPSED_TIME_FAST, QP_TEST_RESULT_QUALITY_WARNING> |
| } |
| } |
| } |
| }; |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Duplicate single pipeline recreation with no explicit cache |
| *//*--------------------------------------------------------------------*/ |
| static constexpr TestParams DUPLICATE_SINGLE_RECREATE_NO_CACHING = |
| { |
| "duplicate_single_recreate_no_caching", |
| "Duplicate single pipeline recreation with no explicit cache", |
| TestParams::NO_CACHE, |
| TestParams::IterationArray{ |
| TestParams::Iteration{ |
| // Iteration [0]: Force compilation of pipeline |
| TestParams::Iteration::SINGLE_NORMAL, |
| ValidatorArray{ |
| // Fail if result is not VK_SUCCESS |
| checkResult<VK_SUCCESS>, |
| // Fail if pipeline is not valid |
| checkPipelineMustBeValid<0> |
| } |
| }, |
| TestParams::Iteration{ |
| // Iteration [1]: Request compilation of same pipeline without compile |
| TestParams::Iteration::SINGLE_NOCOMPILE, |
| ValidatorArray{ |
| // Warn if pipeline took too long |
| checkElapsedTime<ELAPSED_TIME_FAST, QP_TEST_RESULT_QUALITY_WARNING> |
| } |
| } |
| } |
| }; |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Duplicate single pipeline recreation using derivative pipelines |
| *//*--------------------------------------------------------------------*/ |
| static constexpr TestParams DUPLICATE_SINGLE_RECREATE_DERIVATIVE = |
| { |
| "duplicate_single_recreate_derivative", |
| "Duplicate single pipeline recreation using derivative pipelines", |
| TestParams::DERIVATIVE_HANDLE, |
| TestParams::IterationArray{ |
| TestParams::Iteration{ |
| // Iteration [0]: Force compilation of pipeline |
| TestParams::Iteration::SINGLE_NORMAL, |
| ValidatorArray{ |
| // Fail if result is not VK_SUCCESS |
| checkResult<VK_SUCCESS>, |
| // Fail if pipeline is not valid |
| checkPipelineMustBeValid<0> |
| } |
| }, |
| TestParams::Iteration{ |
| // Iteration [1]: Request compilation of same pipeline without compile |
| TestParams::Iteration::SINGLE_NOCOMPILE, |
| ValidatorArray{ |
| // Warn if pipeline took too long |
| checkElapsedTime<ELAPSED_TIME_FAST, QP_TEST_RESULT_QUALITY_WARNING> |
| } |
| } |
| } |
| }; |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Single creation of never before seen pipeline without compile |
| *//*--------------------------------------------------------------------*/ |
| static constexpr TestParams SINGLE_PIPELINE_NO_COMPILE = |
| { |
| "single_pipeline_no_compile", |
| "Single creation of never before seen pipeline without compile", |
| TestParams::NO_CACHE, |
| TestParams::IterationArray{ |
| TestParams::Iteration{ |
| TestParams::Iteration::SINGLE_NOCOMPILE, |
| ValidatorArray{ |
| // Warn if pipeline took too long |
| checkElapsedTime<ELAPSED_TIME_IMMEDIATE, QP_TEST_RESULT_QUALITY_WARNING> |
| } |
| } |
| } |
| }; |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Batch creation of duplicate pipelines with explicit caching |
| *//*--------------------------------------------------------------------*/ |
| static constexpr TestParams DUPLICATE_BATCH_PIPELINES_EXPLICIT_CACHE = |
| { |
| "duplicate_batch_pipelines_explicit_cache", |
| "Batch creation of duplicate pipelines with explicit caching", |
| TestParams::EXPLICIT_CACHE, |
| TestParams::IterationArray{ |
| TestParams::Iteration{ |
| TestParams::Iteration::BATCH_NOCOMPILE_COMPILE_NOCOMPILE, |
| ValidatorArray{ |
| // Fail if pipeline[1] is not valid |
| checkPipelineMustBeValid<1>, |
| // Warn if result is not VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT |
| checkResult<VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT, QP_TEST_RESULT_COMPATIBILITY_WARNING>, |
| // Warn if pipelines[0] is not VK_NULL_HANDLE |
| checkPipelineMustBeNull<0, QP_TEST_RESULT_COMPATIBILITY_WARNING>, |
| // Warn if pipelines[2] is not valid |
| checkPipelineMustBeValid<2, QP_TEST_RESULT_COMPATIBILITY_WARNING> |
| } |
| } |
| } |
| }; |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Batch creation of duplicate pipelines with no caching |
| *//*--------------------------------------------------------------------*/ |
| static constexpr TestParams DUPLICATE_BATCH_PIPELINES_NO_CACHE = |
| { |
| "duplicate_batch_pipelines_no_cache", |
| "Batch creation of duplicate pipelines with no caching", |
| TestParams::EXPLICIT_CACHE, |
| TestParams::IterationArray{ |
| TestParams::Iteration{ |
| TestParams::Iteration::BATCH_NOCOMPILE_COMPILE_NOCOMPILE, |
| ValidatorArray{ |
| // Fail if pipeline[1] is not valid |
| checkPipelineMustBeValid<1>, |
| // Warn if result is not VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT |
| checkResult<VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT, QP_TEST_RESULT_COMPATIBILITY_WARNING>, |
| // Warn if pipelines[0] is not VK_NULL_HANDLE |
| checkPipelineMustBeNull<0, QP_TEST_RESULT_COMPATIBILITY_WARNING> |
| } |
| } |
| } |
| }; |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Batch creation of duplicate pipelines with derivative pipeline index |
| *//*--------------------------------------------------------------------*/ |
| static constexpr TestParams DUPLICATE_BATCH_PIPELINES_DERIVATIVE_INDEX = |
| { |
| "duplicate_batch_pipelines_derivative_index", |
| "Batch creation of duplicate pipelines with derivative pipeline index", |
| TestParams::DERIVATIVE_INDEX, |
| TestParams::IterationArray{ |
| TestParams::Iteration{ |
| TestParams::Iteration::BATCH_NOCOMPILE_COMPILE_NOCOMPILE, |
| ValidatorArray{ |
| // Fail if pipeline[1] is not valid |
| checkPipelineMustBeValid<1>, |
| // Warn if result is not VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT |
| checkResult<VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT, QP_TEST_RESULT_COMPATIBILITY_WARNING>, |
| // Warn if pipelines[0] is not VK_NULL_HANDLE |
| checkPipelineMustBeNull<0, QP_TEST_RESULT_COMPATIBILITY_WARNING> |
| } |
| } |
| } |
| }; |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Batch creation of pipelines with early return |
| *//*--------------------------------------------------------------------*/ |
| static constexpr TestParams BATCH_PIPELINES_EARLY_RETURN = |
| { |
| "batch_pipelines_early_return", |
| "Batch creation of pipelines with early return", |
| TestParams::NO_CACHE, |
| TestParams::IterationArray{ |
| TestParams::Iteration{ |
| TestParams::Iteration::BATCH_RETURN_COMPILE_NOCOMPILE, |
| ValidatorArray{ |
| // fail if a valid pipeline follows the early-return failure |
| checkPipelineNullAfterIndex<0>, |
| // Warn if return was not immediate |
| checkElapsedTime<ELAPSED_TIME_IMMEDIATE, QP_TEST_RESULT_QUALITY_WARNING>, |
| // Warn if pipelines[0] is not VK_NULL_HANDLE |
| checkPipelineMustBeNull<0, QP_TEST_RESULT_COMPATIBILITY_WARNING>, |
| // Warn if result is not VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT |
| checkResult<VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT, QP_TEST_RESULT_COMPATIBILITY_WARNING> |
| } |
| } |
| } |
| }; |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Full array of test cases |
| *//*--------------------------------------------------------------------*/ |
| static constexpr TestParams TEST_CASES[] = |
| { |
| SINGLE_PIPELINE_NO_COMPILE, |
| BATCH_PIPELINES_EARLY_RETURN, |
| DUPLICATE_SINGLE_RECREATE_EXPLICIT_CACHING, |
| DUPLICATE_SINGLE_RECREATE_NO_CACHING, |
| DUPLICATE_SINGLE_RECREATE_DERIVATIVE, |
| DUPLICATE_BATCH_PIPELINES_EXPLICIT_CACHE, |
| DUPLICATE_BATCH_PIPELINES_NO_CACHE, |
| DUPLICATE_BATCH_PIPELINES_DERIVATIVE_INDEX |
| }; |
| // clang-format on |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Variadic version of de::newMovePtr |
| *//*--------------------------------------------------------------------*/ |
| template <typename T, typename... args_t> |
| inline de::MovePtr<T> newMovePtr(args_t&&... args) |
| { |
| return de::MovePtr<T>(new T(::std::forward<args_t>(args)...)); |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Make test group consisting of graphics pipeline tests |
| *//*--------------------------------------------------------------------*/ |
| void addGraphicsPipelineTests(TestCaseGroup& group) |
| { |
| using namespace graphics_tests; |
| |
| auto tests = newMovePtr<TestCaseGroup>( |
| group.getTestContext(), "graphics_pipelines", "Test pipeline creation cache control with graphics pipelines"); |
| |
| for (const auto& params : TEST_CASES) |
| { |
| addFunctionCaseWithPrograms<const TestParams&>( |
| tests.get(), params.name, params.description, checkSupport, initPrograms, testInstance, params); |
| } |
| |
| group.addChild(tests.release()); |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Make test group consisting of compute pipeline tests |
| *//*--------------------------------------------------------------------*/ |
| void addComputePipelineTests(TestCaseGroup& group) |
| { |
| using namespace compute_tests; |
| |
| auto tests = newMovePtr<TestCaseGroup>( |
| group.getTestContext(), "compute_pipelines", "Test pipeline creation cache control with compute pipelines"); |
| |
| for (const auto& params : TEST_CASES) |
| { |
| addFunctionCaseWithPrograms<const TestParams&>( |
| tests.get(), params.name, params.description, checkSupport, initPrograms, testInstance, params); |
| } |
| |
| group.addChild(tests.release()); |
| } |
| |
| } // namespace |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Make pipeline creation cache control test group |
| *//*--------------------------------------------------------------------*/ |
| TestCaseGroup* createCacheControlTests(TestContext& testCtx) |
| { |
| auto tests = newMovePtr<TestCaseGroup>(testCtx, "creation_cache_control", "pipeline creation cache control tests"); |
| |
| addGraphicsPipelineTests(*tests); |
| addComputePipelineTests(*tests); |
| |
| return tests.release(); |
| } |
| |
| } // namespace pipeline |
| |
| } // namespace vkt |