From e1942830e399aaaf2b75a0a0d30bcdbc1945171a Mon Sep 17 00:00:00 2001 From: Tony Barbour Date: Wed, 25 Jan 2017 12:53:48 -0700 Subject: [PATCH] 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 --- layers/buffer_validation.cpp | 95 +++++++++++++----- layers/buffer_validation.h | 14 ++- layers/core_validation.cpp | 222 ++++++++++++++++++++++++++++--------------- 3 files changed, 229 insertions(+), 102 deletions(-) diff --git a/layers/buffer_validation.cpp b/layers/buffer_validation.cpp index bd65e47..632c0ba 100644 --- a/layers/buffer_validation.cpp +++ b/layers/buffer_validation.cpp @@ -62,6 +62,11 @@ void SetLayout(layer_data *device_data, OBJECT *pObject, ImageSubresourcePair im } } +// Set the layout in supplied map +void SetLayout(std::unordered_map &imageLayoutMap, ImageSubresourcePair imgpair, VkImageLayout layout) { + imageLayoutMap[imgpair].layout = layout; +} + bool FindLayoutVerifyNode(layer_data *device_data, GLOBAL_CB_NODE *pCB, ImageSubresourcePair imgpair, IMAGE_CMD_BUF_LAYOUT_NODE &node, const VkImageAspectFlags aspectMask) { const debug_report_data *report_data = core_validation::GetReportData(device_data); @@ -171,6 +176,35 @@ bool FindLayouts(layer_data *device_data, VkImage image, std::vector &imageLayoutMap, ImageSubresourcePair imgpair, VkImageLayout &layout, + const VkImageAspectFlags aspectMask) { + if (!(imgpair.subresource.aspectMask & aspectMask)) { + return false; + } + imgpair.subresource.aspectMask = aspectMask; + auto imgsubIt = imageLayoutMap.find(imgpair); + if (imgsubIt == imageLayoutMap.end()) { + return false; + } + layout = imgsubIt->second.layout; + return true; + } + +// find layout in supplied map +bool FindLayout(const std::unordered_map &imageLayoutMap, ImageSubresourcePair imgpair, VkImageLayout &layout) { + layout = VK_IMAGE_LAYOUT_MAX_ENUM; + FindLayout(imageLayoutMap, imgpair, layout, VK_IMAGE_ASPECT_COLOR_BIT); + FindLayout(imageLayoutMap, imgpair, layout, VK_IMAGE_ASPECT_DEPTH_BIT); + FindLayout(imageLayoutMap, imgpair, layout, VK_IMAGE_ASPECT_STENCIL_BIT); + FindLayout(imageLayoutMap, imgpair, layout, VK_IMAGE_ASPECT_METADATA_BIT); + if (layout == VK_IMAGE_LAYOUT_MAX_ENUM) { + imgpair = {imgpair.image, false, VkImageSubresource()}; + auto imgsubIt = imageLayoutMap.find(imgpair); + if (imgsubIt == imageLayoutMap.end()) return false; + layout = imgsubIt->second.layout; + } + return true; +} // Set the layout on the global level void SetGlobalLayout(layer_data *device_data, ImageSubresourcePair imgpair, const VkImageLayout &layout) { @@ -1718,46 +1752,59 @@ void PreCallRecordCmdBlitImage(layer_data *device_data, GLOBAL_CB_NODE *cb_node, core_validation::UpdateCmdBufferLastCmd(cb_node, CMD_BLITIMAGE); } -// This validates that the initial layout specified in the command buffer for the IMAGE is the same as the global IMAGE layout -bool ValidateCmdBufImageLayouts(core_validation::layer_data *device_data, GLOBAL_CB_NODE *pCB) { - const debug_report_data *report_data = core_validation::GetReportData(device_data); +// This validates that the initial layout specified in the command buffer for +// the IMAGE is the same +// as the global IMAGE layout +bool ValidateCmdBufImageLayouts(layer_data *device_data, GLOBAL_CB_NODE *pCB, std::unordered_map &imageLayoutMap) { bool skip = false; + const debug_report_data *report_data = core_validation::GetReportData(device_data); for (auto cb_image_data : pCB->imageLayoutMap) { VkImageLayout imageLayout; - if (!FindGlobalLayout(device_data, cb_image_data.first, imageLayout)) { - skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, 0, __LINE__, - DRAWSTATE_INVALID_IMAGE_LAYOUT, "DS", "Cannot submit cmd buffer using deleted image 0x%" PRIx64 ".", - reinterpret_cast(cb_image_data.first)); + + if (!FindLayout(imageLayoutMap, cb_image_data.first, imageLayout)) { + skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, + VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, 0, __LINE__, DRAWSTATE_INVALID_IMAGE_LAYOUT, "DS", + "Cannot submit cmd buffer using deleted image 0x%" PRIx64 ".", + reinterpret_cast(cb_image_data.first)); } else { if (cb_image_data.second.initialLayout == VK_IMAGE_LAYOUT_UNDEFINED) { // TODO: Set memory invalid which is in mem_tracker currently } else if (imageLayout != cb_image_data.second.initialLayout) { if (cb_image_data.first.hasSubresource) { - skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - reinterpret_cast(pCB->commandBuffer), __LINE__, DRAWSTATE_INVALID_IMAGE_LAYOUT, - "DS", "Cannot submit cmd buffer using image (0x%" PRIx64 - ") [sub-resource: aspectMask 0x%X array layer %u, mip level %u], " - "with layout %s when first use is %s.", - reinterpret_cast(cb_image_data.first.image), - cb_image_data.first.subresource.aspectMask, cb_image_data.first.subresource.arrayLayer, - cb_image_data.first.subresource.mipLevel, string_VkImageLayout(imageLayout), - string_VkImageLayout(cb_image_data.second.initialLayout)); + skip |= log_msg( + report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, + reinterpret_cast(pCB->commandBuffer), __LINE__, DRAWSTATE_INVALID_IMAGE_LAYOUT, "DS", + "Cannot submit cmd buffer using image (0x%" PRIx64 + ") [sub-resource: aspectMask 0x%X array layer %u, mip level %u], " + "with layout %s when first use is %s.", + reinterpret_cast(cb_image_data.first.image), cb_image_data.first.subresource.aspectMask, + cb_image_data.first.subresource.arrayLayer, cb_image_data.first.subresource.mipLevel, + string_VkImageLayout(imageLayout), string_VkImageLayout(cb_image_data.second.initialLayout)); } else { - skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - reinterpret_cast(pCB->commandBuffer), __LINE__, DRAWSTATE_INVALID_IMAGE_LAYOUT, - "DS", "Cannot submit cmd buffer using image (0x%" PRIx64 - ") with layout %s when " - "first use is %s.", - reinterpret_cast(cb_image_data.first.image), - string_VkImageLayout(imageLayout), string_VkImageLayout(cb_image_data.second.initialLayout)); + skip |= + log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, + VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, reinterpret_cast(pCB->commandBuffer), + __LINE__, DRAWSTATE_INVALID_IMAGE_LAYOUT, "DS", "Cannot submit cmd buffer using image (0x%" PRIx64 + ") with layout %s when " + "first use is %s.", + reinterpret_cast(cb_image_data.first.image), string_VkImageLayout(imageLayout), + string_VkImageLayout(cb_image_data.second.initialLayout)); } } - SetGlobalLayout(device_data, cb_image_data.first, cb_image_data.second.layout); + SetLayout(imageLayoutMap, cb_image_data.first, cb_image_data.second.layout); } } return skip; } +void UpdateCmdBufImageLayouts(layer_data *device_data, GLOBAL_CB_NODE *pCB) { + for (auto cb_image_data : pCB->imageLayoutMap) { + VkImageLayout imageLayout; + FindGlobalLayout(device_data, cb_image_data.first, imageLayout); + SetGlobalLayout(device_data, cb_image_data.first, cb_image_data.second.layout); + } +} + // Print readable FlagBits in FlagMask static std::string string_VkAccessFlags(VkAccessFlags accessMask) { std::string result; diff --git a/layers/buffer_validation.h b/layers/buffer_validation.h index 59617b3..ad5d959 100644 --- a/layers/buffer_validation.h +++ b/layers/buffer_validation.h @@ -76,13 +76,21 @@ bool FindGlobalLayout(layer_data *device_data, ImageSubresourcePair imgpair, VkI bool FindLayouts(layer_data *device_data, VkImage image, std::vector &layouts); +bool FindLayout(const std::unordered_map &imageLayoutMap, ImageSubresourcePair imgpair, VkImageLayout &layout, + const VkImageAspectFlags aspectMask); + +bool FindLayout(const std::unordered_map &imageLayoutMap, ImageSubresourcePair imgpair, VkImageLayout &layout); + void SetGlobalLayout(layer_data *device_data, ImageSubresourcePair imgpair, const VkImageLayout &layout); void SetLayout(layer_data *device_data, GLOBAL_CB_NODE *pCB, ImageSubresourcePair imgpair, const IMAGE_CMD_BUF_LAYOUT_NODE &node); void SetLayout(layer_data *device_data, GLOBAL_CB_NODE *pCB, ImageSubresourcePair imgpair, const VkImageLayout &layout); -void SetImageViewLayout(layer_data *device_data, GLOBAL_CB_NODE *pCB, VkImageView imageView, const VkImageLayout &layout); +void SetLayout(std::unordered_map &imageLayoutMap, ImageSubresourcePair imgpair, VkImageLayout layout); + +void SetImageViewLayout(layer_data *device_data, GLOBAL_CB_NODE *pCB, VkImageView imageView, + const VkImageLayout &layout); bool VerifyFramebufferAndRenderPassLayouts(layer_data *dev_data, GLOBAL_CB_NODE *pCB, const VkRenderPassBeginInfo *pRenderPassBegin, const FRAMEBUFFER_STATE *framebuffer_state); @@ -127,7 +135,9 @@ bool PreCallValidateCmdBlitImage(layer_data *device_data, GLOBAL_CB_NODE *cb_nod void PreCallRecordCmdBlitImage(layer_data *device_data, GLOBAL_CB_NODE *cb_node, IMAGE_STATE *src_image_state, IMAGE_STATE *dst_image_state); -bool ValidateCmdBufImageLayouts(core_validation::layer_data *dev_data, GLOBAL_CB_NODE *pCB); +bool ValidateCmdBufImageLayouts(layer_data *device_data, GLOBAL_CB_NODE *pCB, std::unordered_map &imageLayoutMap); + +void UpdateCmdBufImageLayouts(layer_data *device_data, GLOBAL_CB_NODE *pCB); bool ValidateMaskBitsFromLayouts(core_validation::layer_data *device_data, VkCommandBuffer cmdBuffer, const VkAccessFlags &accessMask, const VkImageLayout &layout, const char *type); diff --git a/layers/core_validation.cpp b/layers/core_validation.cpp index 8dbfbe3..cb9c8c8 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 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 cbs; const VkSubmitInfo *submit = &pSubmits[submit_idx]; vector semaphore_waits; vector 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(), std::vector(), std::vector(), + 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 signaled_semaphores; + unordered_set unsignaled_semaphores; + vector current_cmds; + unordered_map 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(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(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(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(semaphore), reinterpret_cast(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 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(), std::vector(), std::vector(), - 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 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)) { -- 2.7.4