/*------------------------------------------------------------------------ * Vulkan Conformance Tests * ------------------------ * * Copyright (c) 2021 The Khronos Group Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *//*! * \file * \brief Vulkan SC pipeline identifier tests *//*--------------------------------------------------------------------*/ #include "vktPipelineIdentifierTests.hpp" #include #include #include #include "vktTestCaseUtil.hpp" #include "vkDefs.hpp" #include "vkSafetyCriticalUtil.hpp" #include "vkQueryUtil.hpp" #include "vkRefUtil.hpp" #include "tcuTestLog.hpp" namespace vkt { namespace sc { using namespace vk; namespace { enum PIPipeline { PIP_UNUSED = 0, PIP_GRAPHICS, PIP_COMPUTE }; enum PITTestType { PITT_UNUSED = 0, PITT_MISSING_ID, PITT_NONEXISTING_ID, PITT_MATCHCONTROL }; enum PITMatchControl { PIMC_UNUSED = 0, PIMC_UUID_EXACT_MATCH }; struct TestParams { PITTestType type; PITMatchControl matchControl; bool single; }; void createGraphicsShaders (SourceCollections& dst, TestParams testParams) { deUint32 pipelineCount = testParams.single ? 1 : 3; for (deUint32 i = 0; i < pipelineCount; ++i) { std::ostringstream name, code; name << "vertex_" << i; code << "#version 450\n" "\n" "void main (void)\n" "{\n" " gl_Position = vec4( "<< i <<");\n" "}\n"; dst.glslSources.add(name.str()) << glu::VertexSource(code.str()); } for (deUint32 i = 0; i < pipelineCount; ++i) { std::ostringstream name, code; name << "fragment_" << i; code << "#version 450\n" "\n" "layout(location=0) out vec4 x;\n" "void main (void)\n" "{\n" " x = vec4(" << i <<");\n" "}\n"; dst.glslSources.add(name.str()) << glu::FragmentSource(code.str()); } } void createComputeShaders (SourceCollections& dst, TestParams testParams) { deUint32 pipelineCount = testParams.single ? 1 : 3; for (deUint32 i = 0; i < pipelineCount; ++i) { std::ostringstream name, code; name << "compute_" << i; code << "#version 450\n" "layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n" "void main (void)\n" "{\n" " uvec4 x = uvec4(" << i <<");\n" "}\n"; dst.glslSources.add(name.str()) << glu::ComputeSource(code.str()); } } tcu::TestStatus testGraphicsPipelineIdentifier (Context& context, TestParams testParams) { const vk::PlatformInterface& vkp = context.getPlatformInterface(); const InstanceInterface& vki = context.getInstanceInterface(); const VkInstance instance = context.getInstance(); const DeviceInterface& vk = context.getDeviceInterface(); const VkDevice device = context.getDevice(); const VkPhysicalDevice physicalDevice = context.getPhysicalDevice(); deUint32 pipelineCount = testParams.single ? 1 : 3; std::vector> shaders; for (deUint32 i = 0; i < pipelineCount; ++i) { { std::ostringstream name; name << "vertex_" << i; shaders.emplace_back(createShaderModule(vk, device, context.getBinaryCollection().get(name.str()), 0)); } { std::ostringstream name; name << "fragment_" << i; shaders.emplace_back(createShaderModule(vk, device, context.getBinaryCollection().get(name.str()), 0)); } } std::vector> shaderStageCreateInfos (pipelineCount); for (deUint32 i = 0; i < pipelineCount; ++i) { shaderStageCreateInfos[i].push_back ( { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // VkStructureType sType; DE_NULL, // const void* pNext; (VkPipelineShaderStageCreateFlags)0, // VkPipelineShaderStageCreateFlags flags; VK_SHADER_STAGE_VERTEX_BIT, // VkShaderStageFlagBits stage; *shaders[2*i], // VkShaderModule shader; "main", // const char* pName; DE_NULL, // const VkSpecializationInfo* pSpecializationInfo; } ); shaderStageCreateInfos[i].push_back ( { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // VkStructureType sType; DE_NULL, // const void* pNext; (VkPipelineShaderStageCreateFlags)0, // VkPipelineShaderStageCreateFlags flags; VK_SHADER_STAGE_FRAGMENT_BIT, // VkShaderStageFlagBits stage; *shaders[2*i+1], // VkShaderModule shader; "main", // const char* pName; DE_NULL, // const VkSpecializationInfo* pSpecializationInfo; } ); } std::vector vertexInputStateCreateInfo (pipelineCount); std::vector inputAssemblyStateCreateInfo (pipelineCount); std::vector viewPortStateCreateInfo (pipelineCount); std::vector rasterizationStateCreateInfo (pipelineCount); std::vector multisampleStateCreateInfo (pipelineCount); std::vector colorBlendAttachmentState (pipelineCount); std::vector colorBlendStateCreateInfo (pipelineCount); std::vector dynamicStateCreateInfo (pipelineCount); std::vector> dynamicStates { { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }, { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }, { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }, }; const VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // VkStructureType sType; DE_NULL, // const void* pNext; (VkPipelineLayoutCreateFlags)0u, // VkPipelineLayoutCreateFlags flags; 0u, // deUint32 setLayoutCount; DE_NULL, // const VkDescriptorSetLayout* pSetLayouts; 0u, // deUint32 pushConstantRangeCount; DE_NULL // const VkPushConstantRange* pPushConstantRanges; }; Move pipelineLayout = createPipelineLayout(vk, device, &pipelineLayoutCreateInfo); const VkFormat format = getRenderTargetFormat(vki, physicalDevice); VkAttachmentDescription attachmentDescription; VkAttachmentReference attachmentReference; VkSubpassDescription subpassDescription; VkRenderPassCreateInfo renderPassCreateInfo = prepareSimpleRenderPassCI(format, attachmentDescription, attachmentReference, subpassDescription); Move renderPass = createRenderPass(vk, device, &renderPassCreateInfo); std::vector graphicsPipelineCreateInfos (pipelineCount); for (deUint32 i = 0; i < pipelineCount; ++i) graphicsPipelineCreateInfos[i] = prepareSimpleGraphicsPipelineCI(vertexInputStateCreateInfo[i], shaderStageCreateInfos[i], inputAssemblyStateCreateInfo[i], viewPortStateCreateInfo[i], rasterizationStateCreateInfo[i], multisampleStateCreateInfo[i], colorBlendAttachmentState[i], colorBlendStateCreateInfo[i], dynamicStateCreateInfo[i], dynamicStates[i], *pipelineLayout, *renderPass); std::vector sourcePID { "IDG_0000", "IDG_1111", "IDG_2222" }; std::vector destPID; switch (testParams.type) { case PITT_MISSING_ID: case PITT_NONEXISTING_ID: destPID = { "IDG_XXXX", "IDG_1111", "IDG_2222" }; break; case PITT_MATCHCONTROL: switch (testParams.matchControl) { case PIMC_UUID_EXACT_MATCH: destPID = { "IDG_0000", "IDG_1111", "IDG_2222" }; break; default: TCU_THROW(InternalError, "Unrecognized match control"); } break; default: TCU_THROW(InternalError, "Unrecognized test type"); } // fill pipeline identifiers with initial values, apply pipeline names from sourcePID std::vector pipelineIDs; for (deUint32 i = 0; i < pipelineCount; ++i) { pipelineIDs.emplace_back(resetPipelineOfflineCreateInfo()); applyPipelineIdentifier(pipelineIDs[i], sourcePID[i]); } switch (testParams.matchControl) { case PIMC_UUID_EXACT_MATCH: for (deUint32 i = 0; i < pipelineCount; ++i) pipelineIDs[i].matchControl = VK_PIPELINE_MATCH_CONTROL_APPLICATION_UUID_EXACT_MATCH; break; default: TCU_THROW(InternalError, "Unrecognized match control"); } if (!context.getTestContext().getCommandLine().isSubProcess()) { // If it's a main process - we create graphics pipelines only to increase VkDeviceObjectReservationCreateInfo::computePipelineRequestCount. // We also fill all pipeline identifiers with distinct values ( otherwise the framework will create pipeline identifiers itself ) for (deUint32 i = 0; i < pipelineCount; ++i) { pipelineIDs[i].pNext = graphicsPipelineCreateInfos[i].pNext; graphicsPipelineCreateInfos[i].pNext = &pipelineIDs[i]; } std::vector> pipelines; for (deUint32 i = 0; i < pipelineCount; ++i) pipelines.emplace_back(createGraphicsPipeline(vk, device, DE_NULL, &graphicsPipelineCreateInfos[i])); return tcu::TestStatus::pass("Pass"); } for (deUint32 i = 0; i < pipelineCount; ++i) context.getResourceInterface()->fillPoolEntrySize(pipelineIDs[i]); // subprocess - we create the same pipeline, but we use vkCreateGraphicsPipelines directly to skip the framework GetDeviceProcAddrFunc getDeviceProcAddrFunc = (GetDeviceProcAddrFunc)vkp.getInstanceProcAddr(instance, "vkGetDeviceProcAddr"); CreateGraphicsPipelinesFunc createGraphicsPipelinesFunc = (CreateGraphicsPipelinesFunc)getDeviceProcAddrFunc(device, "vkCreateGraphicsPipelines"); DestroyPipelineFunc destroyPipelineFunc = (DestroyPipelineFunc)getDeviceProcAddrFunc(device, "vkDestroyPipeline"); VkPipelineCache pipelineCache = context.getResourceInterface()->getPipelineCache(device); std::vector pipelines (pipelineCount); VkResult expectedResult; std::vector expectedNullHandle (pipelineCount); switch (testParams.type) { case PITT_MISSING_ID: expectedResult = VK_ERROR_NO_PIPELINE_MATCH; expectedNullHandle[0] = 1; for (deUint32 i = 1; i < pipelineCount; ++i) { // we are skipping pipeline identifier at index 0 applyPipelineIdentifier(pipelineIDs[i], destPID[i]); pipelineIDs[i].pNext = graphicsPipelineCreateInfos[i].pNext; graphicsPipelineCreateInfos[i].pNext = &pipelineIDs[i]; expectedNullHandle[i] = 0; } break; case PITT_NONEXISTING_ID: expectedResult = VK_ERROR_NO_PIPELINE_MATCH; for (deUint32 i = 0; i < pipelineCount; ++i) { // Pipeline identifier at index 0 uses wrong ID for PITT_NONEXISTING_ID test // or a proper one for PITT_MATCHCONTROL test applyPipelineIdentifier(pipelineIDs[i], destPID[i]); pipelineIDs[i].pNext = graphicsPipelineCreateInfos[i].pNext; graphicsPipelineCreateInfos[i].pNext = &pipelineIDs[i]; expectedNullHandle[i] = (i == 0); } break; case PITT_MATCHCONTROL: expectedResult = VK_SUCCESS; for (deUint32 i = 0; i < pipelineCount; ++i) { // Pipeline identifier at index 0 uses wrong ID for PITT_NONEXISTING_ID test // or a proper one for PITT_MATCHCONTROL test applyPipelineIdentifier(pipelineIDs[i], destPID[i]); pipelineIDs[i].pNext = graphicsPipelineCreateInfos[i].pNext; graphicsPipelineCreateInfos[i].pNext = &pipelineIDs[i]; expectedNullHandle[i] = 0; } break; default: TCU_THROW(InternalError, "Unrecognized match control"); } VkResult result = createGraphicsPipelinesFunc(device, pipelineCache, pipelineCount, graphicsPipelineCreateInfos.data(), DE_NULL, pipelines.data()); bool isOK = true; for (deUint32 i = 0; i < pipelineCount; ++i) { if (expectedNullHandle[i] == 0 && pipelines[i] == DE_NULL) { context.getTestContext().getLog() << tcu::TestLog::Message << "Pipeline "<< i << " should be created" << tcu::TestLog::EndMessage; isOK = false; } if (expectedNullHandle[i] != 0 && pipelines[i] != DE_NULL) { context.getTestContext().getLog() << tcu::TestLog::Message << "Pipeline " << i << " should not be created" << tcu::TestLog::EndMessage; isOK = false; } } if (result != expectedResult) { context.getTestContext().getLog() << tcu::TestLog::Message << "vkCreateGraphicsPipelines returned wrong VkResult" << tcu::TestLog::EndMessage; isOK = false; } for (deUint32 i = 0; i < pipelineCount; ++i) destroyPipelineFunc(device, pipelines[i], DE_NULL); return isOK ? tcu::TestStatus::pass("Pass") : tcu::TestStatus::fail("Fail"); } tcu::TestStatus testComputePipelineIdentifier (Context& context, TestParams testParams) { const vk::PlatformInterface& vkp = context.getPlatformInterface(); const VkInstance instance = context.getInstance(); const DeviceInterface& vk = context.getDeviceInterface(); const VkDevice device = context.getDevice(); deUint32 pipelineCount = testParams.single ? 1 : 3; std::vector> computeShaders; for (deUint32 i = 0; i < pipelineCount; ++i) { std::ostringstream name; name << "compute_" << i; computeShaders.emplace_back(createShaderModule(vk, device, context.getBinaryCollection().get(name.str()), 0)); } std::vector shaderStageCreateInfos (pipelineCount); for (deUint32 i = 0; i < pipelineCount; ++i) { shaderStageCreateInfos[i] = { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // VkStructureType sType; DE_NULL, // const void* pNext; (VkPipelineShaderStageCreateFlags)0, // VkPipelineShaderStageCreateFlags flags; VK_SHADER_STAGE_COMPUTE_BIT, // VkShaderStageFlagBits stage; *computeShaders[i], // VkShaderModule shader; "main", // const char* pName; DE_NULL, // const VkSpecializationInfo* pSpecializationInfo; }; } const VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // VkStructureType sType; DE_NULL, // const void* pNext; (VkPipelineLayoutCreateFlags)0u, // VkPipelineLayoutCreateFlags flags; 0u, // deUint32 setLayoutCount; DE_NULL, // const VkDescriptorSetLayout* pSetLayouts; 0u, // deUint32 pushConstantRangeCount; DE_NULL // const VkPushConstantRange* pPushConstantRanges; }; Move pipelineLayout = createPipelineLayout(vk, device, &pipelineLayoutCreateInfo); std::vector computePipelineCreateInfos (pipelineCount); for (deUint32 i = 0; i < pipelineCount; ++i) computePipelineCreateInfos[i] = prepareSimpleComputePipelineCI(shaderStageCreateInfos[i], *pipelineLayout); std::vector sourcePID { "IDC_0000", "IDC_1111", "IDC_2222" }; std::vector destPID; switch (testParams.type) { case PITT_MISSING_ID: case PITT_NONEXISTING_ID: destPID = { "IDC_XXXX", "IDC_1111", "IDC_2222" }; break; case PITT_MATCHCONTROL: switch (testParams.matchControl) { case PIMC_UUID_EXACT_MATCH: destPID = { "IDC_0000", "IDC_1111", "IDC_2222" }; break; default: TCU_THROW(InternalError, "Unrecognized match control"); } break; default: TCU_THROW(InternalError, "Unrecognized test type"); } // fill pipeline identifiers with initial values, apply pipeline names from sourcePID std::vector pipelineIDs; for (deUint32 i = 0; i < pipelineCount; ++i) { pipelineIDs.emplace_back(resetPipelineOfflineCreateInfo()); applyPipelineIdentifier(pipelineIDs[i], sourcePID[i]); } switch (testParams.matchControl) { case PIMC_UUID_EXACT_MATCH: for (deUint32 i = 0; i < pipelineCount; ++i) pipelineIDs[i].matchControl = VK_PIPELINE_MATCH_CONTROL_APPLICATION_UUID_EXACT_MATCH; break; default: TCU_THROW(InternalError, "Unrecognized match control"); } if (!context.getTestContext().getCommandLine().isSubProcess()) { // If it's a main process - we create compute pipelines only to increase VkDeviceObjectReservationCreateInfo::computePipelineRequestCount. // We also fill all pipeline identifiers with distinct values ( otherwise the framework will create pipeline identifiers itself ) for (deUint32 i = 0; i < pipelineCount; ++i) { pipelineIDs[i].pNext = computePipelineCreateInfos[i].pNext; computePipelineCreateInfos[i].pNext = &pipelineIDs[i]; } std::vector> pipelines; for (deUint32 i = 0; i < pipelineCount; ++i) pipelines.emplace_back(createComputePipeline(vk, device, DE_NULL, &computePipelineCreateInfos[i])); return tcu::TestStatus::pass("Pass"); } for (deUint32 i = 0; i < pipelineCount; ++i) context.getResourceInterface()->fillPoolEntrySize(pipelineIDs[i]); // In subprocess we create the same pipelines, but we use vkCreateGraphicsPipelines directly to skip the framework GetDeviceProcAddrFunc getDeviceProcAddrFunc = (GetDeviceProcAddrFunc)vkp.getInstanceProcAddr(instance, "vkGetDeviceProcAddr"); CreateComputePipelinesFunc createComputePipelinesFunc = (CreateComputePipelinesFunc)getDeviceProcAddrFunc(device, "vkCreateComputePipelines"); DestroyPipelineFunc destroyPipelineFunc = (DestroyPipelineFunc)getDeviceProcAddrFunc(device, "vkDestroyPipeline"); VkPipelineCache pipelineCache = context.getResourceInterface()->getPipelineCache(device); std::vector pipelines (pipelineCount); VkResult expectedResult; std::vector expectedNullHandle (pipelineCount); switch (testParams.type) { case PITT_MISSING_ID: expectedResult = VK_ERROR_NO_PIPELINE_MATCH; expectedNullHandle[0] = 1; for (deUint32 i = 1; i < pipelineCount; ++i) { // we are skipping pipeline identifier at index 0 applyPipelineIdentifier(pipelineIDs[i], destPID[i]); pipelineIDs[i].pNext = computePipelineCreateInfos[i].pNext; computePipelineCreateInfos[i].pNext = &pipelineIDs[i]; expectedNullHandle[i] = 0; } break; case PITT_NONEXISTING_ID: expectedResult = VK_ERROR_NO_PIPELINE_MATCH; for (deUint32 i = 0; i < pipelineCount; ++i) { // Pipeline identifier at index 0 uses wrong ID for PITT_NONEXISTING_ID test // or a proper one for PITT_MATCHCONTROL test applyPipelineIdentifier(pipelineIDs[i], destPID[i]); pipelineIDs[i].pNext = computePipelineCreateInfos[i].pNext; computePipelineCreateInfos[i].pNext = &pipelineIDs[i]; expectedNullHandle[i] = (i == 0); } break; case PITT_MATCHCONTROL: expectedResult = VK_SUCCESS; for (deUint32 i = 0; i < pipelineCount; ++i) { // Pipeline identifier at index 0 uses wrong ID for PITT_NONEXISTING_ID test // or a proper one for PITT_MATCHCONTROL test applyPipelineIdentifier(pipelineIDs[i], destPID[i]); pipelineIDs[i].pNext = computePipelineCreateInfos[i].pNext; computePipelineCreateInfos[i].pNext = &pipelineIDs[i]; expectedNullHandle[i] = 0; } break; default: TCU_THROW(InternalError, "Unrecognized match control"); } VkResult result = createComputePipelinesFunc(device, pipelineCache, pipelineCount, computePipelineCreateInfos.data(), DE_NULL, pipelines.data()); bool isOK = true; for (deUint32 i = 0; i < pipelineCount; ++i) { if (expectedNullHandle[i] == 0 && pipelines[i] == DE_NULL) { context.getTestContext().getLog() << tcu::TestLog::Message << "Pipeline "<< i << " should be created" << tcu::TestLog::EndMessage; isOK = false; } if (expectedNullHandle[i] != 0 && pipelines[i] != DE_NULL) { context.getTestContext().getLog() << tcu::TestLog::Message << "Pipeline " << i << " should not be created" << tcu::TestLog::EndMessage; isOK = false; } } if (result != expectedResult) { context.getTestContext().getLog() << tcu::TestLog::Message << "vkCreateComputePipelines returned wrong VkResult" << tcu::TestLog::EndMessage; isOK = false; } for (deUint32 i = 0; i < pipelineCount; ++i) destroyPipelineFunc(device, pipelines[i], DE_NULL); return isOK ? tcu::TestStatus::pass("Pass") : tcu::TestStatus::fail("Fail"); } } // anonymous tcu::TestCaseGroup* createPipelineIdentifierTests (tcu::TestContext& testCtx) { // Tests verifying Vulkan SC pipeline identifier structure de::MovePtr group(new tcu::TestCaseGroup(testCtx, "pipeline_identifier")); const struct { PIPipeline pipeline; const char* name; FunctionPrograms1::Function initPrograms; FunctionInstance1::Function testFunction; } pipelineTypes[] = { { PIP_GRAPHICS, "graphics", createGraphicsShaders, testGraphicsPipelineIdentifier }, { PIP_COMPUTE, "compute", createComputeShaders, testComputePipelineIdentifier }, }; const struct { PITTestType type; const char* name; } testTypes[] = { { PITT_MISSING_ID, "missing_pid" }, { PITT_NONEXISTING_ID, "nonexisting_pid" }, { PITT_MATCHCONTROL, "match_control" }, }; const struct { PITMatchControl control; const char* name; } matchControls[] = { { PIMC_UUID_EXACT_MATCH, "exact_match" }, }; const struct { bool single; const char* name; } cardinalities[] = { { true, "single" }, { false, "multiple" }, }; for (int pipelineIdx = 0; pipelineIdx < DE_LENGTH_OF_ARRAY(pipelineTypes); ++pipelineIdx) { de::MovePtr pipelineGroup(new tcu::TestCaseGroup(testCtx, pipelineTypes[pipelineIdx].name)); for (int typeIdx = 0; typeIdx < DE_LENGTH_OF_ARRAY(testTypes); ++typeIdx) { de::MovePtr typeGroup(new tcu::TestCaseGroup(testCtx, testTypes[typeIdx].name)); for (int matchIdx = 0; matchIdx < DE_LENGTH_OF_ARRAY(matchControls); ++matchIdx) { de::MovePtr matchGroup(new tcu::TestCaseGroup(testCtx, matchControls[matchIdx].name)); for (int cardIdx = 0; cardIdx < DE_LENGTH_OF_ARRAY(cardinalities); ++cardIdx) { TestParams testParams{ testTypes[typeIdx].type, matchControls[matchIdx].control, cardinalities[cardIdx].single }; addFunctionCaseWithPrograms(matchGroup.get(), cardinalities[cardIdx].name, pipelineTypes[pipelineIdx].initPrograms, pipelineTypes[pipelineIdx].testFunction, testParams); } typeGroup->addChild(matchGroup.release()); } pipelineGroup->addChild(typeGroup.release()); } group->addChild(pipelineGroup.release()); } return group.release(); } } // sc } // vkt