blob: fae8df314a454e9d150e66d9e94734dca1877762 [file] [log] [blame]
/*------------------------------------------------------------------------
* 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 <set>
#include <vector>
#include <string>
#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<VkShaderModule> vertexShader = createShaderModule(vk, device, context.getBinaryCollection().get("vertex"), 0);
Move<VkShaderModule> fragmentShader = createShaderModule(vk, device, context.getBinaryCollection().get("fragment"), 0);
std::vector<VkPipelineShaderStageCreateInfo> 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<VkDynamicState> 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<VkPipelineLayout> 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<VkRenderPass> 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<VkPipeline> graphicsPipeline = createGraphicsPipeline(vk, device, DE_NULL, &graphicsPipelineCreateInfo);
}
// compute pipeline
{
Move<VkShaderModule> 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<VkPipelineLayout> 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<VkPipeline> 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<deUint8> 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<VkPipelinePoolSize> 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<VkDevice> device;
{
std::vector<const char*> enabledLayers;
if (deviceCreateInfo.enabledLayerCount == 0u && context.getTestContext().getCommandLine().isValidationEnabled())
{
enabledLayers = getValidationLayers(instanceDriver, physicalDevice);
deviceCreateInfo.enabledLayerCount = static_cast<deUint32>(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<VkDevice>(check<VkDevice>(object), Deleter<VkDevice>(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<tcu::TestCaseGroup> 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