From b2847c02a903eb329e6e0f2cf7cc6155144253d6 Mon Sep 17 00:00:00 2001 From: Maciej Jesionowski Date: Tue, 22 Nov 2016 12:30:45 +0100 Subject: [PATCH] Add tests for bulk object allocation failures New tests: - dEQP-VK.api.object_management.alloc_callback_fail_multiple.* Issue #532 Change-Id: I1a8050c8000abf083435910e2e65c6ee045364af --- android/cts/master/vk-master.txt | 5 + .../vulkan/api/vktApiObjectManagementTests.cpp | 252 ++++++++++++++++++++- external/vulkancts/mustpass/1.0.2/vk-default.txt | 5 + 3 files changed, 260 insertions(+), 2 deletions(-) diff --git a/android/cts/master/vk-master.txt b/android/cts/master/vk-master.txt index 65ba872..2cd7aa9 100644 --- a/android/cts/master/vk-master.txt +++ b/android/cts/master/vk-master.txt @@ -1686,6 +1686,11 @@ dEQP-VK.api.object_management.alloc_callback_fail.command_pool dEQP-VK.api.object_management.alloc_callback_fail.command_pool_transient dEQP-VK.api.object_management.alloc_callback_fail.command_buffer_primary dEQP-VK.api.object_management.alloc_callback_fail.command_buffer_secondary +dEQP-VK.api.object_management.alloc_callback_fail_multiple.graphics_pipeline +dEQP-VK.api.object_management.alloc_callback_fail_multiple.compute_pipeline +dEQP-VK.api.object_management.alloc_callback_fail_multiple.descriptor_set +dEQP-VK.api.object_management.alloc_callback_fail_multiple.command_buffer_primary +dEQP-VK.api.object_management.alloc_callback_fail_multiple.command_buffer_secondary dEQP-VK.api.buffer.create_buffer_1_0 dEQP-VK.api.buffer.create_buffer_2_0 dEQP-VK.api.buffer.create_buffer_3_0 diff --git a/external/vulkancts/modules/vulkan/api/vktApiObjectManagementTests.cpp b/external/vulkancts/modules/vulkan/api/vktApiObjectManagementTests.cpp index 771f996..9f0b019 100644 --- a/external/vulkancts/modules/vulkan/api/vktApiObjectManagementTests.cpp +++ b/external/vulkancts/modules/vulkan/api/vktApiObjectManagementTests.cpp @@ -73,6 +73,10 @@ using tcu::TestLog; using std::string; using std::vector; +typedef SharedPtr > VkPipelineSp; // Move so it's possible to disown the handle +typedef SharedPtr > VkDescriptorSetSp; +typedef SharedPtr > VkCommandBufferSp; + class ThreadGroupThread; /*--------------------------------------------------------------------*//*! @@ -1529,8 +1533,12 @@ struct GraphicsPipeline ShaderModule::initPrograms(dst, ShaderModule::Parameters(VK_SHADER_STAGE_FRAGMENT_BIT, "frag")); } - static Move create (const Environment& env, const Resources& res, const Parameters&) + static vector createMultiple (const Environment& env, const Resources& res, const Parameters&, vector* const pOutHandles, VkResult* const pOutResult) { + DE_ASSERT(pOutResult); + DE_ASSERT(pOutHandles); + DE_ASSERT(pOutHandles->size() != 0); + const VkPipelineShaderStageCreateInfo stages[] = { { @@ -1695,7 +1703,32 @@ struct GraphicsPipeline 0, // basePipelineIndex }; - return createGraphicsPipeline(env.vkd, env.device, *res.pipelineCache.object, &pipelineInfo, env.allocationCallbacks); + const deUint32 numPipelines = static_cast(pOutHandles->size()); + VkPipeline* const pHandles = &(*pOutHandles)[0]; + vector pipelineInfos (numPipelines, pipelineInfo); + + *pOutResult = env.vkd.createGraphicsPipelines(env.device, *res.pipelineCache.object, numPipelines, &pipelineInfos[0], env.allocationCallbacks, pHandles); + + vector pipelines; + + // Even if an error is returned, some pipelines may have been created successfully + for (deUint32 i = 0; i < numPipelines; ++i) + { + if (pHandles[i] != DE_NULL) + pipelines.push_back(VkPipelineSp(new Move(check(pHandles[i]), Deleter(env.vkd, env.device, env.allocationCallbacks)))); + } + + return pipelines; + } + + static Move create (const Environment& env, const Resources& res, const Parameters&) + { + vector handles (1, DE_NULL); + VkResult result = VK_NOT_READY; + vector scopedHandles = createMultiple(env, res, Parameters(), &handles, &result); + + VK_CHECK(result); + return Move(check(scopedHandles.front()->disown()), Deleter(env.vkd, env.device, env.allocationCallbacks)); } }; @@ -1767,6 +1800,49 @@ struct ComputePipeline return createComputePipeline(env.vkd, env.device, *res.pipelineCache.object, &pipelineInfo, env.allocationCallbacks); } + + static vector createMultiple (const Environment& env, const Resources& res, const Parameters&, vector* const pOutHandles, VkResult* const pOutResult) + { + DE_ASSERT(pOutResult); + DE_ASSERT(pOutHandles); + DE_ASSERT(pOutHandles->size() != 0); + + const VkComputePipelineCreateInfo commonPipelineInfo = + { + VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, + DE_NULL, + (VkPipelineCreateFlags)0, + { + VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + DE_NULL, + (VkPipelineShaderStageCreateFlags)0, + VK_SHADER_STAGE_COMPUTE_BIT, + *res.shaderModule.object, + "main", + DE_NULL // pSpecializationInfo + }, + *res.layout.object, + (VkPipeline)0, // basePipelineHandle + 0u, // basePipelineIndex + }; + + const deUint32 numPipelines = static_cast(pOutHandles->size()); + VkPipeline* const pHandles = &(*pOutHandles)[0]; + vector pipelineInfos (numPipelines, commonPipelineInfo); + + *pOutResult = env.vkd.createComputePipelines(env.device, *res.pipelineCache.object, numPipelines, &pipelineInfos[0], env.allocationCallbacks, pHandles); + + vector pipelines; + + // Even if an error is returned, some pipelines may have been created successfully + for (deUint32 i = 0; i < numPipelines; ++i) + { + if (pHandles[i] != DE_NULL) + pipelines.push_back(VkPipelineSp(new Move(check(pHandles[i]), Deleter(env.vkd, env.device, env.allocationCallbacks)))); + } + + return pipelines; + } }; struct DescriptorPool @@ -1891,6 +1967,38 @@ struct DescriptorSet return allocateDescriptorSet(env.vkd, env.device, &allocateInfo); } + + static vector createMultiple (const Environment& env, const Resources& res, const Parameters&, vector* const pOutHandles, VkResult* const pOutResult) + { + DE_ASSERT(pOutResult); + DE_ASSERT(pOutHandles); + DE_ASSERT(pOutHandles->size() != 0); + + const deUint32 numDescriptorSets = static_cast(pOutHandles->size()); + VkDescriptorSet* const pHandles = &(*pOutHandles)[0]; + const vector descriptorSetLayouts (numDescriptorSets, res.descriptorSetLayout.object.get()); + + const VkDescriptorSetAllocateInfo allocateInfo = + { + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + DE_NULL, + *res.descriptorPool.object, + numDescriptorSets, + &descriptorSetLayouts[0], + }; + + *pOutResult = env.vkd.allocateDescriptorSets(env.device, &allocateInfo, pHandles); + + vector descriptorSets; + + if (*pOutResult == VK_SUCCESS) + { + for (deUint32 i = 0; i < numDescriptorSets; ++i) + descriptorSets.push_back(VkDescriptorSetSp(new Move(check(pHandles[i]), Deleter(env.vkd, env.device, *res.descriptorPool.object)))); + } + + return descriptorSets; + } }; struct Framebuffer @@ -2044,6 +2152,37 @@ struct CommandBuffer return allocateCommandBuffer(env.vkd, env.device, &cmdBufferInfo); } + + static vector createMultiple (const Environment& env, const Resources& res, const Parameters& params, vector* const pOutHandles, VkResult* const pOutResult) + { + DE_ASSERT(pOutResult); + DE_ASSERT(pOutHandles); + DE_ASSERT(pOutHandles->size() != 0); + + const deUint32 numCommandBuffers = static_cast(pOutHandles->size()); + VkCommandBuffer* const pHandles = &(*pOutHandles)[0]; + + const VkCommandBufferAllocateInfo cmdBufferInfo = + { + VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + DE_NULL, + *res.commandPool.object, + params.level, + numCommandBuffers, + }; + + *pOutResult = env.vkd.allocateCommandBuffers(env.device, &cmdBufferInfo, pHandles); + + vector commandBuffers; + + if (*pOutResult == VK_SUCCESS) + { + for (deUint32 i = 0; i < numCommandBuffers; ++i) + commandBuffers.push_back(VkCommandBufferSp(new Move(check(pHandles[i]), Deleter(env.vkd, env.device, *res.commandPool.object)))); + } + + return commandBuffers; + } }; // Test cases @@ -2379,6 +2518,85 @@ tcu::TestStatus allocCallbackFailTest (Context& context, typename Object::Parame return tcu::TestStatus::pass("Ok"); } +// Determine whether an API call sets the invalid handles to NULL (true) or leaves them undefined or not modified (false) +template +inline bool isNullHandleOnAllocationFailure (void) { return false; } + +template<> +inline bool isNullHandleOnAllocationFailure (void) { return true; } + +template +tcu::TestStatus allocCallbackFailMultipleObjectsTest (Context& context, typename Object::Parameters params) +{ + typedef SharedPtr > ObjectTypeSp; + + static const deUint32 numObjects = 4; + const bool expectNullHandles = isNullHandleOnAllocationFailure(); + deUint32 numPassingAllocs = 0; + + { + vector handles (numObjects); + VkResult result = VK_NOT_READY; + + for (; numPassingAllocs <= numObjects; ++numPassingAllocs) + { + ValidateQueryBits::fillBits(handles.begin(), handles.end()); // fill with garbage + + // \note We have to use the same allocator for both resource dependencies and the object under test, + // because pooled objects take memory from the pool. + DeterministicFailAllocator objAllocator(getSystemAllocator(), numPassingAllocs, DeterministicFailAllocator::MODE_DO_NOT_COUNT); + AllocationCallbackRecorder recorder (objAllocator.getCallbacks(), 128); + const Environment objEnv (context.getPlatformInterface(), + context.getDeviceInterface(), + context.getDevice(), + context.getUniversalQueueFamilyIndex(), + context.getBinaryCollection(), + recorder.getCallbacks(), + numObjects); + + context.getTestContext().getLog() + << TestLog::Message + << "Trying to create " << numObjects << " objects with " << numPassingAllocs << " allocation" << (numPassingAllocs != 1 ? "s" : "") << " passing" + << TestLog::EndMessage; + + { + const typename Object::Resources res (objEnv, params); + + objAllocator.setMode(DeterministicFailAllocator::MODE_COUNT_AND_FAIL); + const vector scopedHandles = Object::createMultiple(objEnv, res, params, &handles, &result); + } + + if (result == VK_SUCCESS) + { + context.getTestContext().getLog() << TestLog::Message << "Construction of all objects succeeded! " << TestLog::EndMessage; + break; + } + else + { + if (expectNullHandles) + { + for (deUint32 nullNdx = numPassingAllocs; nullNdx < numObjects; ++nullNdx) + { + if (handles[nullNdx] != DE_NULL) + return tcu::TestStatus::fail("Some object handles weren't set to NULL"); + } + } + + if (result != VK_ERROR_OUT_OF_HOST_MEMORY) + return tcu::TestStatus::fail("Got invalid error code: " + de::toString(getResultName(result))); + + if (!validateAndLog(context.getTestContext().getLog(), recorder, 0u)) + return tcu::TestStatus::fail("Invalid allocation callback"); + } + } + } + + if (numPassingAllocs == 0) + return tcu::TestStatus(QP_TEST_RESULT_QUALITY_WARNING, "Allocation callbacks not called"); + else + return tcu::TestStatus::pass("Ok"); +} + // Utilities for creating groups template @@ -2873,6 +3091,36 @@ tcu::TestCaseGroup* createObjectManagementTests (tcu::TestContext& testCtx) }; objectMgmtTests->addChild(createGroup(testCtx, "alloc_callback_fail", "Allocation callback failure", s_allocCallbackFailGroup)); + // \note Test objects that can be created in bulk + static const CaseDescriptions s_allocCallbackFailMultipleObjectsGroup = + { + EMPTY_CASE_DESC(Instance), // most objects can be created one at a time only + EMPTY_CASE_DESC(Device), + EMPTY_CASE_DESC(DeviceMemory), + EMPTY_CASE_DESC(Buffer), + EMPTY_CASE_DESC(BufferView), + EMPTY_CASE_DESC(Image), + EMPTY_CASE_DESC(ImageView), + EMPTY_CASE_DESC(Semaphore), + EMPTY_CASE_DESC(Event), + EMPTY_CASE_DESC(Fence), + EMPTY_CASE_DESC(QueryPool), + EMPTY_CASE_DESC(ShaderModule), + EMPTY_CASE_DESC(PipelineCache), + EMPTY_CASE_DESC(PipelineLayout), + EMPTY_CASE_DESC(RenderPass), + CASE_DESC(allocCallbackFailMultipleObjectsTest , s_graphicsPipelineCases), + CASE_DESC(allocCallbackFailMultipleObjectsTest , s_computePipelineCases), + EMPTY_CASE_DESC(DescriptorSetLayout), + EMPTY_CASE_DESC(Sampler), + EMPTY_CASE_DESC(DescriptorPool), + CASE_DESC(allocCallbackFailMultipleObjectsTest , s_descriptorSetCases), + EMPTY_CASE_DESC(Framebuffer), + EMPTY_CASE_DESC(CommandPool), + CASE_DESC(allocCallbackFailMultipleObjectsTest , s_commandBufferCases), + }; + objectMgmtTests->addChild(createGroup(testCtx, "alloc_callback_fail_multiple", "Allocation callback failure creating multiple objects with one call", s_allocCallbackFailMultipleObjectsGroup)); + return objectMgmtTests.release(); } diff --git a/external/vulkancts/mustpass/1.0.2/vk-default.txt b/external/vulkancts/mustpass/1.0.2/vk-default.txt index ab1f103..e7d4882 100644 --- a/external/vulkancts/mustpass/1.0.2/vk-default.txt +++ b/external/vulkancts/mustpass/1.0.2/vk-default.txt @@ -1686,6 +1686,11 @@ dEQP-VK.api.object_management.alloc_callback_fail.command_pool dEQP-VK.api.object_management.alloc_callback_fail.command_pool_transient dEQP-VK.api.object_management.alloc_callback_fail.command_buffer_primary dEQP-VK.api.object_management.alloc_callback_fail.command_buffer_secondary +dEQP-VK.api.object_management.alloc_callback_fail_multiple.graphics_pipeline +dEQP-VK.api.object_management.alloc_callback_fail_multiple.compute_pipeline +dEQP-VK.api.object_management.alloc_callback_fail_multiple.descriptor_set +dEQP-VK.api.object_management.alloc_callback_fail_multiple.command_buffer_primary +dEQP-VK.api.object_management.alloc_callback_fail_multiple.command_buffer_secondary dEQP-VK.api.buffer.create_buffer_1_0 dEQP-VK.api.buffer.create_buffer_2_0 dEQP-VK.api.buffer.create_buffer_3_0 -- 2.7.4