/*------------------------------------------------------------------------ * 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 cache tests *//*--------------------------------------------------------------------*/ #include "vktPipelineCacheSCTests.hpp" #include #include #include #include "vktTestCaseUtil.hpp" #include "vktCustomInstancesDevices.hpp" #include "vkDefs.hpp" #include "vkDeviceUtil.hpp" #include "vkQueryUtil.hpp" #include "vkRefUtil.hpp" #include "vkSafetyCriticalUtil.hpp" #include "tcuTestLog.hpp" namespace vkt { namespace sc { using namespace vk; namespace { enum PipelineCacheTestType { PCTT_UNUSED = 0, PCTT_WRONG_VENDOR_ID, PCTT_WRONG_DEVICE_ID }; struct TestParams { PipelineCacheTestType type; }; void createShaders (SourceCollections& dst, TestParams params) { DE_UNREF(params); { std::ostringstream name, code; name << "vertex"; code << "#version 450\n" "\n" "void main (void)\n" "{\n" " gl_Position = vec4( 1.0 );\n" "}\n"; dst.glslSources.add(name.str()) << glu::VertexSource(code.str()); } { std::ostringstream name, code; name << "fragment"; code << "#version 450\n" "\n" "layout(location=0) out vec4 x;\n" "void main (void)\n" "{\n" " x = vec4( 1.0 );\n" "}\n"; dst.glslSources.add(name.str()) << glu::FragmentSource(code.str()); } { std::ostringstream name, code; name << "compute"; 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( 1 );\n" "}\n"; dst.glslSources.add(name.str()) << glu::ComputeSource(code.str()); } } tcu::TestStatus createPipelineCacheTest (Context& context, TestParams testParams) { const vk::PlatformInterface& vkp = context.getPlatformInterface(); const CustomInstance instance (createCustomInstanceFromContext(context)); const InstanceDriver& instanceDriver (instance.getDriver()); const VkPhysicalDevice physicalDevice = chooseDevice(instanceDriver, instance, context.getTestContext().getCommandLine()); std::string graphicsPID = "PCST_GRAPHICS"; std::string computePID = "PCST_COMPUTE"; // In main process : prepare one graphics pipeline and one compute pipeline // Actually these pipelines are here only to ensure that pipeline cache is not empty. We don't use them in subprocess if (!context.getTestContext().getCommandLine().isSubProcess()) { const DeviceInterface& vk = context.getDeviceInterface(); const VkDevice device = context.getDevice(); // graphics pipeline { Move vertexShader = createShaderModule(vk, device, context.getBinaryCollection().get("vertex"), 0); Move fragmentShader = createShaderModule(vk, device, context.getBinaryCollection().get("fragment"), 0); std::vector shaderStageCreateInfos; shaderStageCreateInfos.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; *vertexShader, // VkShaderModule shader; "main", // const char* pName; DE_NULL, // const VkSpecializationInfo* pSpecializationInfo; } ); shaderStageCreateInfos.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; *fragmentShader, // VkShaderModule shader; "main", // const char* pName; DE_NULL, // const VkSpecializationInfo* pSpecializationInfo; } ); VkPipelineVertexInputStateCreateInfo vertexInputStateCreateInfo; VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCreateInfo; VkPipelineViewportStateCreateInfo viewPortStateCreateInfo; VkPipelineRasterizationStateCreateInfo rasterizationStateCreateInfo; VkPipelineMultisampleStateCreateInfo multisampleStateCreateInfo; VkPipelineColorBlendAttachmentState colorBlendAttachmentState; VkPipelineColorBlendStateCreateInfo colorBlendStateCreateInfo; VkPipelineDynamicStateCreateInfo dynamicStateCreateInfo; std::vector dynamicStates{ 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(instanceDriver, physicalDevice); VkAttachmentDescription attachmentDescription; VkAttachmentReference attachmentReference; VkSubpassDescription subpassDescription; VkRenderPassCreateInfo renderPassCreateInfo = prepareSimpleRenderPassCI(format, attachmentDescription, attachmentReference, subpassDescription); Move renderPass = createRenderPass(vk, device, &renderPassCreateInfo); VkGraphicsPipelineCreateInfo graphicsPipelineCreateInfo = prepareSimpleGraphicsPipelineCI(vertexInputStateCreateInfo, shaderStageCreateInfos, inputAssemblyStateCreateInfo, viewPortStateCreateInfo, rasterizationStateCreateInfo, multisampleStateCreateInfo, colorBlendAttachmentState, colorBlendStateCreateInfo, dynamicStateCreateInfo, dynamicStates, *pipelineLayout, *renderPass); // connect pipeline identifier VkPipelineOfflineCreateInfo pipelineID = resetPipelineOfflineCreateInfo(); applyPipelineIdentifier(pipelineID, graphicsPID); pipelineID.pNext = graphicsPipelineCreateInfo.pNext; graphicsPipelineCreateInfo.pNext = &pipelineID; // creation of a pipeline in main process registers it in pipeline cache Move graphicsPipeline = createGraphicsPipeline(vk, device, DE_NULL, &graphicsPipelineCreateInfo); } // compute pipeline { Move computeShader = createShaderModule(vk, device, context.getBinaryCollection().get("compute"), 0); VkPipelineShaderStageCreateInfo shaderStageCreateInfo = { 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; *computeShader, // 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); VkComputePipelineCreateInfo computePipelineCreateInfo = prepareSimpleComputePipelineCI(shaderStageCreateInfo, *pipelineLayout); // connect pipeline identifier VkPipelineOfflineCreateInfo pipelineID = resetPipelineOfflineCreateInfo(); applyPipelineIdentifier(pipelineID, computePID); pipelineID.pNext = computePipelineCreateInfo.pNext; computePipelineCreateInfo.pNext = &pipelineID; // creation of a pipeline in main process registers it in pipeline cache Move computePipeline = createComputePipeline(vk, device, DE_NULL, &computePipelineCreateInfo); } return tcu::TestStatus::pass("Pass"); } // Subprocess : prepare pipeline cache data according to test type // Copy data from ResourceInterface std::vector customCacheData (context.getResourceInterface()->getCacheDataSize()); deMemcpy(customCacheData.data(), context.getResourceInterface()->getCacheData(), context.getResourceInterface()->getCacheDataSize()); deUintptr initialDataSize = customCacheData.size(); VkPipelineCacheHeaderVersionSafetyCriticalOne* pcHeader = (VkPipelineCacheHeaderVersionSafetyCriticalOne*)customCacheData.data(); switch (testParams.type) { case PCTT_WRONG_VENDOR_ID: { pcHeader->headerVersionOne.vendorID = deUint32(VK_VENDOR_ID_MAX_ENUM); break; } case PCTT_WRONG_DEVICE_ID: { pcHeader->headerVersionOne.deviceID = 0xFFFFFFFF; break; } default: TCU_THROW(InternalError, "Unrecognized test type"); } // Now, create custom device const float queuePriority = 1.0f; const VkDeviceQueueCreateInfo deviceQueueCI = { VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, // sType DE_NULL, // pNext (VkDeviceQueueCreateFlags)0u, // flags 0, //queueFamilyIndex; 1, //queueCount; &queuePriority, //pQueuePriorities; }; VkDeviceCreateInfo deviceCreateInfo = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, // sType; DE_NULL, // pNext; (VkDeviceCreateFlags)0u, // flags 1, // queueRecordCount; &deviceQueueCI, // pRequestedQueues; 0, // layerCount; DE_NULL, // ppEnabledLayerNames; 0, // extensionCount; DE_NULL, // ppEnabledExtensionNames; DE_NULL, // pEnabledFeatures; }; VkDeviceObjectReservationCreateInfo objectInfo = resetDeviceObjectReservationCreateInfo(); objectInfo.pNext = DE_NULL; objectInfo.pipelineLayoutRequestCount = 2u; objectInfo.renderPassRequestCount = 1u; objectInfo.subpassDescriptionRequestCount = 1u; objectInfo.attachmentDescriptionRequestCount = 1u; objectInfo.graphicsPipelineRequestCount = 1u; objectInfo.computePipelineRequestCount = 1u; objectInfo.pipelineCacheRequestCount = 2u; VkPipelineCacheCreateInfo pipelineCacheCreateInfo = { VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO, // VkStructureType sType; DE_NULL, // const void* pNext; VK_PIPELINE_CACHE_CREATE_READ_ONLY_BIT | VK_PIPELINE_CACHE_CREATE_USE_APPLICATION_STORAGE_BIT, // VkPipelineCacheCreateFlags flags; initialDataSize, // deUintptr initialDataSize; customCacheData.data() // const void* pInitialData; }; objectInfo.pipelineCacheCreateInfoCount = 1u; objectInfo.pPipelineCacheCreateInfos = &pipelineCacheCreateInfo; std::vector poolSizes = context.getResourceInterface()->getPipelinePoolSizes(); if (!poolSizes.empty()) { objectInfo.pipelinePoolSizeCount = deUint32(poolSizes.size()); objectInfo.pPipelinePoolSizes = poolSizes.data(); } void* pNext = &objectInfo; VkPhysicalDeviceVulkanSC10Features sc10Features = createDefaultSC10Features(); sc10Features.pNext = pNext; pNext = &sc10Features; deviceCreateInfo.pNext = pNext; tcu::TestStatus testStatus = tcu::TestStatus::pass("Pass"); Move device; { std::vector enabledLayers; if (deviceCreateInfo.enabledLayerCount == 0u && context.getTestContext().getCommandLine().isValidationEnabled()) { enabledLayers = getValidationLayers(instanceDriver, physicalDevice); deviceCreateInfo.enabledLayerCount = static_cast(enabledLayers.size()); deviceCreateInfo.ppEnabledLayerNames = (enabledLayers.empty() ? DE_NULL : enabledLayers.data()); } VkDevice object = 0; VkResult result = instanceDriver.createDevice(physicalDevice, &deviceCreateInfo, DE_NULL, &object); switch (testParams.type) { case PCTT_WRONG_VENDOR_ID: case PCTT_WRONG_DEVICE_ID: if (result != VK_ERROR_INVALID_PIPELINE_CACHE_DATA) testStatus = tcu::TestStatus::fail("Fail"); break; default: TCU_THROW(InternalError, "Unrecognized test type"); } if (result != VK_SUCCESS) return testStatus; device = Move(check(object), Deleter(vkp, instance, object, DE_NULL)); } // create our own pipeline cache in subprocess. Use VK functions directly GetDeviceProcAddrFunc getDeviceProcAddrFunc = (GetDeviceProcAddrFunc)vkp.getInstanceProcAddr(instance, "vkGetDeviceProcAddr"); CreatePipelineCacheFunc createPipelineCacheFunc = (CreatePipelineCacheFunc)getDeviceProcAddrFunc(*device, "vkCreatePipelineCache"); DestroyPipelineCacheFunc destroyPipelineCacheFunc = (DestroyPipelineCacheFunc)getDeviceProcAddrFunc(*device, "vkDestroyPipelineCache"); VkPipelineCache pipelineCache; VkResult result = createPipelineCacheFunc(*device, &pipelineCacheCreateInfo, DE_NULL, &pipelineCache); switch (testParams.type) { case PCTT_WRONG_VENDOR_ID: case PCTT_WRONG_DEVICE_ID: if (result != VK_ERROR_INVALID_PIPELINE_CACHE_DATA) testStatus = tcu::TestStatus::fail("Fail"); break; default: TCU_THROW(InternalError, "Unrecognized test type"); } if (result == VK_SUCCESS) destroyPipelineCacheFunc(*device, pipelineCache, DE_NULL); return testStatus; } } // anonymous tcu::TestCaseGroup* createPipelineCacheTests (tcu::TestContext& testCtx) { de::MovePtr group(new tcu::TestCaseGroup(testCtx, "pipeline_cache", "Tests verifying Vulkan SC pipeline cache")); const struct { PipelineCacheTestType testType; const char* name; } tests[] = { { PCTT_WRONG_VENDOR_ID, "incorrect_vendor_id" }, { PCTT_WRONG_DEVICE_ID, "incorrect_device_id" }, }; for (int testIdx = 0; testIdx < DE_LENGTH_OF_ARRAY(tests); ++testIdx) { TestParams params{ tests[testIdx].testType }; addFunctionCaseWithPrograms(group.get(), tests[testIdx].name, "", createShaders, createPipelineCacheTest, params); } return group.release(); } } // sc } // vkt