aboutsummaryrefslogtreecommitdiff
path: root/layers/core_validation.cpp
diff options
context:
space:
mode:
authorTony Barbour <tony@LunarG.com>2017-01-25 12:53:48 -0700
committerTony Barbour <tony@LunarG.com>2017-02-13 11:57:29 -0700
commite1942830e399aaaf2b75a0a0d30bcdbc1945171a (patch)
treeb00dbae1b557fffb9949fb312ad70d54d899b048 /layers/core_validation.cpp
parent99a86825c8b22db7d9727678befffe8ab94a251c (diff)
downloadusermoji-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.cpp222
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)) {