diff options
| author | Tony Barbour <tony@LunarG.com> | 2017-01-25 12:53:48 -0700 |
|---|---|---|
| committer | Tony Barbour <tony@LunarG.com> | 2017-02-13 11:57:29 -0700 |
| commit | e1942830e399aaaf2b75a0a0d30bcdbc1945171a (patch) | |
| tree | b00dbae1b557fffb9949fb312ad70d54d899b048 /layers/core_validation.cpp | |
| parent | 99a86825c8b22db7d9727678befffe8ab94a251c (diff) | |
| download | usermoji-e1942830e399aaaf2b75a0a0d30bcdbc1945171a.tar.xz | |
layers: Separate validation from state in QueueSubmit
Change-Id: I95878805bfc025afd60ba2167aeb78dba064b56e
layers: Rework semaphore signal tracking in QueueSubmit
Change-Id: I6ee12fd44ebd42c1a4e14bb7fd0eae300489d413
layers: Better submit count tracking in QueueSubmit
Change-Id: I90065fc4546354cb2be14be9143356132ac2f3df
layers: Improve image layout tracking in QueueSubmit
Change-Id: I6e10c8a6d18730939dfa0a5d5e452a23c540f94e
layers: Move cmd collection from validate to record
Change-Id: I89df7358f1916ea4688d4b8cfe2cb870f946b3d9
layers: Fix command buffer submit count in QueueSubmit
Use vector and count instead of unordered_map
Change-Id: I227951085af7df0c288cb3563d17a04d7f2f41e1
Diffstat (limited to 'layers/core_validation.cpp')
| -rw-r--r-- | layers/core_validation.cpp | 222 |
1 files changed, 146 insertions, 76 deletions
diff --git a/layers/core_validation.cpp b/layers/core_validation.cpp index 8dbfbe35..cb9c8c8a 100644 --- a/layers/core_validation.cpp +++ b/layers/core_validation.cpp @@ -3987,9 +3987,9 @@ static bool ValidateStageMaskGsTsEnables(layer_data *dev_data, VkPipelineStageFl return skip; } -// Loop through bound objects and increment their in_use counts -// For any unknown objects, flag an error -static bool ValidateAndIncrementBoundObjects(layer_data *dev_data, GLOBAL_CB_NODE const *cb_node) { +// Loop through bound objects and increment their in_use counts if increment parameter is true +// or flag an error if unknown objects are found +static bool ValidateOrIncrementBoundObjects(layer_data *dev_data, GLOBAL_CB_NODE const *cb_node, bool increment) { bool skip = false; DRAW_STATE_ERROR error_code = DRAWSTATE_NONE; BASE_NODE *base_obj = nullptr; @@ -4069,37 +4069,32 @@ static bool ValidateAndIncrementBoundObjects(layer_data *dev_data, GLOBAL_CB_NOD // TODO : Merge handling of other objects types into this code break; } - if (!base_obj) { + if (base_obj && increment) { + base_obj->in_use.fetch_add(1); + } else if (!base_obj && !increment) { skip |= log_msg(dev_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, obj.type, obj.handle, __LINE__, error_code, "DS", "Cannot submit cmd buffer using deleted %s 0x%" PRIx64 ".", object_type_to_string(obj.type), obj.handle); - } else { - base_obj->in_use.fetch_add(1); } } return skip; } - // Track which resources are in-flight by atomically incrementing their "in_use" count -static bool validateAndIncrementResources(layer_data *dev_data, GLOBAL_CB_NODE *cb_node) { - bool skip_call = false; +static void incrementResources(layer_data *dev_data, GLOBAL_CB_NODE *cb_node) { + cb_node->submitCount++; cb_node->in_use.fetch_add(1); dev_data->globalInFlightCmdBuffers.insert(cb_node->commandBuffer); // First Increment for all "generic" objects bound to cmd buffer, followed by special-case objects below - skip_call |= ValidateAndIncrementBoundObjects(dev_data, cb_node); + ValidateOrIncrementBoundObjects(dev_data, cb_node, true); // TODO : We should be able to remove the NULL look-up checks from the code below as long as // all the corresponding cases are verified to cause CB_INVALID state and the CB_INVALID state // should then be flagged prior to calling this function for (auto drawDataElement : cb_node->drawData) { for (auto buffer : drawDataElement.buffers) { auto buffer_state = GetBufferState(dev_data, buffer); - if (!buffer_state) { - skip_call |= log_msg(dev_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT, - (uint64_t)(buffer), __LINE__, DRAWSTATE_INVALID_BUFFER, "DS", - "Cannot submit cmd buffer using deleted buffer 0x%" PRIx64 ".", (uint64_t)(buffer)); - } else { + if (buffer_state) { buffer_state->in_use.fetch_add(1); } } @@ -4108,7 +4103,6 @@ static bool validateAndIncrementResources(layer_data *dev_data, GLOBAL_CB_NODE * auto event_state = GetEventNode(dev_data, event); if (event_state) event_state->write_in_use++; } - return skip_call; } // Note: This function assumes that the global lock is held by the calling thread. @@ -4258,9 +4252,9 @@ static void SubmitFence(QUEUE_STATE *pQueue, FENCE_NODE *pFence, uint64_t submit pFence->signaler.second = pQueue->seq + pQueue->submissions.size() + submitCount; } -static bool validateCommandBufferSimultaneousUse(layer_data *dev_data, GLOBAL_CB_NODE *pCB) { +static bool validateCommandBufferSimultaneousUse(layer_data *dev_data, GLOBAL_CB_NODE *pCB, int current_submit_count) { bool skip_call = false; - if (dev_data->globalInFlightCmdBuffers.count(pCB->commandBuffer) && + if ((dev_data->globalInFlightCmdBuffers.count(pCB->commandBuffer) || current_submit_count > 1) && !(pCB->beginInfo.flags & VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT)) { skip_call |= log_msg(dev_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, 0, __LINE__, VALIDATION_ERROR_00133, "DS", @@ -4270,16 +4264,16 @@ static bool validateCommandBufferSimultaneousUse(layer_data *dev_data, GLOBAL_CB return skip_call; } -static bool validateCommandBufferState(layer_data *dev_data, GLOBAL_CB_NODE *pCB, const char *call_source) { +static bool validateCommandBufferState(layer_data *dev_data, GLOBAL_CB_NODE *pCB, const char *call_source, int current_submit_count) { bool skip = false; if (dev_data->instance_data->disabled.command_buffer_state) return skip; // Validate ONE_TIME_SUBMIT_BIT CB is not being submitted more than once - if ((pCB->beginInfo.flags & VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT) && (pCB->submitCount > 1)) { + if ((pCB->beginInfo.flags & VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT) && (pCB->submitCount + current_submit_count > 1)) { skip |= log_msg(dev_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, 0, __LINE__, DRAWSTATE_COMMAND_BUFFER_SINGLE_SUBMIT_VIOLATION, "DS", "Commandbuffer 0x%p was begun w/ VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT " "set, but has been submitted 0x%" PRIxLEAST64 " times.", - pCB->commandBuffer, pCB->submitCount); + pCB->commandBuffer, pCB->submitCount + current_submit_count); } // Validate that cmd buffers have been updated if (CB_RECORDED != pCB->state) { @@ -4307,6 +4301,26 @@ static bool validateCommandBufferState(layer_data *dev_data, GLOBAL_CB_NODE *pCB return skip; } +static bool validateResources(layer_data *dev_data, GLOBAL_CB_NODE *cb_node) { + bool skip_call = false; + + skip_call |= ValidateOrIncrementBoundObjects(dev_data, cb_node, false); + // TODO : We should be able to remove the NULL look-up checks from the code below as long as + // all the corresponding cases are verified to cause CB_INVALID state and the CB_INVALID state + // should then be flagged prior to calling this function + for (auto drawDataElement : cb_node->drawData) { + for (auto buffer : drawDataElement.buffers) { + auto buffer_state = GetBufferState(dev_data, buffer); + if (!buffer_state) { + skip_call |= log_msg(dev_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT, + (uint64_t)(buffer), __LINE__, DRAWSTATE_INVALID_BUFFER, "DS", + "Cannot submit cmd buffer using deleted buffer 0x%" PRIx64 ".", (uint64_t)(buffer)); + } + } + } + return skip_call; +} + // Validate that queueFamilyIndices of primary command buffers match this queue // Secondary command buffers were previously validated in vkCmdExecuteCommands(). static bool validateQueueFamilyIndices(layer_data *dev_data, GLOBAL_CB_NODE *pCB, VkQueue queue) { @@ -4326,20 +4340,20 @@ static bool validateQueueFamilyIndices(layer_data *dev_data, GLOBAL_CB_NODE *pCB return skip_call; } -static bool validatePrimaryCommandBufferState(layer_data *dev_data, GLOBAL_CB_NODE *pCB) { +static bool validatePrimaryCommandBufferState(layer_data *dev_data, GLOBAL_CB_NODE *pCB, int current_submit_count) { // Track in-use for resources off of primary and any secondary CBs bool skip_call = false; // If USAGE_SIMULTANEOUS_USE_BIT not set then CB cannot already be executing // on device - skip_call |= validateCommandBufferSimultaneousUse(dev_data, pCB); + skip_call |= validateCommandBufferSimultaneousUse(dev_data, pCB, current_submit_count); - skip_call |= validateAndIncrementResources(dev_data, pCB); + skip_call |= validateResources(dev_data, pCB); if (!pCB->secondaryCommandBuffers.empty()) { for (auto secondaryCmdBuffer : pCB->secondaryCommandBuffers) { GLOBAL_CB_NODE *pSubCB = GetCBNode(dev_data, secondaryCmdBuffer); - skip_call |= validateAndIncrementResources(dev_data, pSubCB); + skip_call |= validateResources(dev_data, pSubCB); if ((pSubCB->primaryCommandBuffer != pCB->commandBuffer) && !(pSubCB->beginInfo.flags & VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT)) { log_msg( @@ -4353,7 +4367,7 @@ static bool validatePrimaryCommandBufferState(layer_data *dev_data, GLOBAL_CB_NO } } - skip_call |= validateCommandBufferState(dev_data, pCB, "vkQueueSubmit()"); + skip_call |= validateCommandBufferState(dev_data, pCB, "vkQueueSubmit()", current_submit_count); return skip_call; } @@ -4382,49 +4396,106 @@ static bool ValidateFenceForSubmit(layer_data *dev_data, FENCE_NODE *pFence) { return skip_call; } -VKAPI_ATTR VkResult VKAPI_CALL QueueSubmit(VkQueue queue, uint32_t submitCount, const VkSubmitInfo *pSubmits, VkFence fence) { - bool skip_call = false; - layer_data *dev_data = GetLayerDataPtr(get_dispatch_key(queue), layer_data_map); - VkResult result = VK_ERROR_VALIDATION_FAILED_EXT; - std::unique_lock<std::mutex> lock(global_lock); - +static void PostCallRecordQueueSubmit(layer_data *dev_data, VkQueue queue, uint32_t submitCount, const VkSubmitInfo *pSubmits, + VkFence fence) { auto pQueue = GetQueueState(dev_data, queue); auto pFence = GetFenceNode(dev_data, fence); - skip_call |= ValidateFenceForSubmit(dev_data, pFence); - - if (skip_call) { - return VK_ERROR_VALIDATION_FAILED_EXT; - } // Mark the fence in-use. if (pFence) { SubmitFence(pQueue, pFence, std::max(1u, submitCount)); } - // Now verify each individual submit + // Now process each individual submit for (uint32_t submit_idx = 0; submit_idx < submitCount; submit_idx++) { + std::vector<VkCommandBuffer> cbs; const VkSubmitInfo *submit = &pSubmits[submit_idx]; vector<SEMAPHORE_WAIT> semaphore_waits; vector<VkSemaphore> semaphore_signals; for (uint32_t i = 0; i < submit->waitSemaphoreCount; ++i) { + VkSemaphore semaphore = submit->pWaitSemaphores[i]; + auto pSemaphore = GetSemaphoreNode(dev_data, semaphore); + if (pSemaphore) { + if (pSemaphore->signaler.first != VK_NULL_HANDLE) { + semaphore_waits.push_back({semaphore, pSemaphore->signaler.first, pSemaphore->signaler.second}); + pSemaphore->in_use.fetch_add(1); + } + pSemaphore->signaler.first = VK_NULL_HANDLE; + pSemaphore->signaled = false; + } + } + for (uint32_t i = 0; i < submit->signalSemaphoreCount; ++i) { + VkSemaphore semaphore = submit->pSignalSemaphores[i]; + auto pSemaphore = GetSemaphoreNode(dev_data, semaphore); + if (pSemaphore) { + pSemaphore->signaler.first = queue; + pSemaphore->signaler.second = pQueue->seq + pQueue->submissions.size() + 1; + pSemaphore->signaled = true; + pSemaphore->in_use.fetch_add(1); + semaphore_signals.push_back(semaphore); + } + } + for (uint32_t i = 0; i < submit->commandBufferCount; i++) { + auto cb_node = GetCBNode(dev_data, submit->pCommandBuffers[i]); + if (cb_node) { + cbs.push_back(submit->pCommandBuffers[i]); + for (auto secondaryCmdBuffer : cb_node->secondaryCommandBuffers) { + cbs.push_back(secondaryCmdBuffer); + } + UpdateCmdBufImageLayouts(dev_data, cb_node); + incrementResources(dev_data, cb_node); + if (!cb_node->secondaryCommandBuffers.empty()) { + for (auto secondaryCmdBuffer : cb_node->secondaryCommandBuffers) { + GLOBAL_CB_NODE *pSubCB = GetCBNode(dev_data, secondaryCmdBuffer); + incrementResources(dev_data, pSubCB); + } + } + } + } + pQueue->submissions.emplace_back(cbs, semaphore_waits, semaphore_signals, + submit_idx == submitCount - 1 ? fence : VK_NULL_HANDLE); + } + + if (pFence && !submitCount) { + // If no submissions, but just dropping a fence on the end of the queue, + // record an empty submission with just the fence, so we can determine + // its completion. + pQueue->submissions.emplace_back(std::vector<VkCommandBuffer>(), std::vector<SEMAPHORE_WAIT>(), std::vector<VkSemaphore>(), + fence); + } +} + +static bool PreCallValidateQueueSubmit(layer_data *dev_data, VkQueue queue, uint32_t submitCount, const VkSubmitInfo *pSubmits, + VkFence fence) { + auto pFence = GetFenceNode(dev_data, fence); + bool skip_call = ValidateFenceForSubmit(dev_data, pFence); + if (skip_call) { + return true; + } + + unordered_set<VkSemaphore> signaled_semaphores; + unordered_set<VkSemaphore> unsignaled_semaphores; + vector<VkCommandBuffer> current_cmds; + unordered_map<ImageSubresourcePair, IMAGE_LAYOUT_NODE> localImageLayoutMap = dev_data->imageLayoutMap; + // Now verify each individual submit + for (uint32_t submit_idx = 0; submit_idx < submitCount; submit_idx++) { + const VkSubmitInfo *submit = &pSubmits[submit_idx]; + for (uint32_t i = 0; i < submit->waitSemaphoreCount; ++i) { skip_call |= ValidateStageMaskGsTsEnables(dev_data, submit->pWaitDstStageMask[i], "vkQueueSubmit()", VALIDATION_ERROR_00142, VALIDATION_ERROR_00143); VkSemaphore semaphore = submit->pWaitSemaphores[i]; auto pSemaphore = GetSemaphoreNode(dev_data, semaphore); if (pSemaphore) { - if (pSemaphore->signaled) { - if (pSemaphore->signaler.first != VK_NULL_HANDLE) { - semaphore_waits.push_back({semaphore, pSemaphore->signaler.first, pSemaphore->signaler.second}); - pSemaphore->in_use.fetch_add(1); - } - pSemaphore->signaler.first = VK_NULL_HANDLE; - pSemaphore->signaled = false; - } else { + if (unsignaled_semaphores.count(semaphore) || + (!(signaled_semaphores.count(semaphore)) && !(pSemaphore->signaled))) { skip_call |= log_msg(dev_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_SEMAPHORE_EXT, reinterpret_cast<const uint64_t &>(semaphore), __LINE__, DRAWSTATE_QUEUE_FORWARD_PROGRESS, "DS", "Queue 0x%p is waiting on semaphore 0x%" PRIx64 " that has no way to be signaled.", queue, reinterpret_cast<const uint64_t &>(semaphore)); + } else { + signaled_semaphores.erase(semaphore); + unsignaled_semaphores.insert(semaphore); } } } @@ -4432,7 +4503,8 @@ VKAPI_ATTR VkResult VKAPI_CALL QueueSubmit(VkQueue queue, uint32_t submitCount, VkSemaphore semaphore = submit->pSignalSemaphores[i]; auto pSemaphore = GetSemaphoreNode(dev_data, semaphore); if (pSemaphore) { - if (pSemaphore->signaled) { + if (signaled_semaphores.count(semaphore) || + (!(unsignaled_semaphores.count(semaphore)) && pSemaphore->signaled)) { skip_call |= log_msg(dev_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_SEMAPHORE_EXT, reinterpret_cast<const uint64_t &>(semaphore), __LINE__, DRAWSTATE_QUEUE_FORWARD_PROGRESS, "DS", @@ -4441,31 +4513,25 @@ VKAPI_ATTR VkResult VKAPI_CALL QueueSubmit(VkQueue queue, uint32_t submitCount, queue, reinterpret_cast<const uint64_t &>(semaphore), reinterpret_cast<uint64_t &>(pSemaphore->signaler.first)); } else { - pSemaphore->signaler.first = queue; - pSemaphore->signaler.second = pQueue->seq + pQueue->submissions.size() + 1; - pSemaphore->signaled = true; - pSemaphore->in_use.fetch_add(1); - semaphore_signals.push_back(semaphore); + unsignaled_semaphores.erase(semaphore); + signaled_semaphores.insert(semaphore); } } } - - std::vector<VkCommandBuffer> cbs; - for (uint32_t i = 0; i < submit->commandBufferCount; i++) { auto cb_node = GetCBNode(dev_data, submit->pCommandBuffers[i]); - skip_call |= ValidateCmdBufImageLayouts(dev_data, cb_node); + skip_call |= ValidateCmdBufImageLayouts(dev_data, cb_node, localImageLayoutMap); if (cb_node) { - cbs.push_back(submit->pCommandBuffers[i]); - for (auto secondaryCmdBuffer : cb_node->secondaryCommandBuffers) { - cbs.push_back(secondaryCmdBuffer); - } - - cb_node->submitCount++; // increment submit count - skip_call |= validatePrimaryCommandBufferState(dev_data, cb_node); + current_cmds.push_back(submit->pCommandBuffers[i]); + skip_call |= validatePrimaryCommandBufferState( + dev_data, cb_node, (int)std::count(current_cmds.begin(), current_cmds.end(), submit->pCommandBuffers[i])); skip_call |= validateQueueFamilyIndices(dev_data, cb_node, queue); + // Potential early exit here as bad object state may crash in delayed function calls - if (skip_call) return result; + if (skip_call) { + return true; + } + // Call submit-time functions to validate/update state for (auto &function : cb_node->validate_functions) { skip_call |= function(); @@ -4478,22 +4544,26 @@ VKAPI_ATTR VkResult VKAPI_CALL QueueSubmit(VkQueue queue, uint32_t submitCount, } } } - - pQueue->submissions.emplace_back(cbs, semaphore_waits, semaphore_signals, - submit_idx == submitCount - 1 ? fence : VK_NULL_HANDLE); } + return skip_call; +} - if (pFence && !submitCount) { - // If no submissions, but just dropping a fence on the end of the queue, - // record an empty submission with just the fence, so we can determine - // its completion. - pQueue->submissions.emplace_back(std::vector<VkCommandBuffer>(), std::vector<SEMAPHORE_WAIT>(), std::vector<VkSemaphore>(), - fence); - } +VKAPI_ATTR VkResult VKAPI_CALL QueueSubmit(VkQueue queue, uint32_t submitCount, const VkSubmitInfo *pSubmits, VkFence fence) { + layer_data *dev_data = GetLayerDataPtr(get_dispatch_key(queue), layer_data_map); + std::unique_lock<std::mutex> lock(global_lock); + + bool skip = PreCallValidateQueueSubmit(dev_data, queue, submitCount, pSubmits, fence); lock.unlock(); - if (!skip_call) result = dev_data->dispatch_table.QueueSubmit(queue, submitCount, pSubmits, fence); + if (skip) + return VK_ERROR_VALIDATION_FAILED_EXT; + + VkResult result = dev_data->dispatch_table.QueueSubmit(queue, submitCount, pSubmits, fence); + + lock.lock(); + PostCallRecordQueueSubmit(dev_data, queue, submitCount, pSubmits, fence); + lock.unlock(); return result; } @@ -9732,7 +9802,7 @@ VKAPI_ATTR void VKAPI_CALL CmdExecuteCommands(VkCommandBuffer commandBuffer, uin } // TODO(mlentine): Move more logic into this method skip_call |= validateSecondaryCommandBufferState(dev_data, pCB, pSubCB); - skip_call |= validateCommandBufferState(dev_data, pSubCB, "vkCmdExecuteCommands()"); + skip_call |= validateCommandBufferState(dev_data, pSubCB, "vkCmdExecuteCommands()", 0); // Secondary cmdBuffers are considered pending execution starting w/ // being recorded if (!(pSubCB->beginInfo.flags & VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT)) { |
