syncval: Add fence wait support
Add tracking for queue submit fences and wait operations for known
signaled fences.
diff --git a/layers/synchronization_validation.cpp b/layers/synchronization_validation.cpp
index 2df8b20..56022f2 100644
--- a/layers/synchronization_validation.cpp
+++ b/layers/synchronization_validation.cpp
@@ -860,6 +860,15 @@
}
}
+template <typename Predicate>
+void AccessContext::EraseIf(Predicate &&pred) {
+ for (const auto address_type : kAddressTypes) {
+ auto &accesses = GetAccessStateMap(address_type);
+ // Note: Don't forward, we don't want r-values moved, since we're going to make multiple calls.
+ layer_data::EraseIf(accesses, pred);
+ }
+}
+
// A recursive range walker for hazard detection, first for the current context and the (DetectHazardRecur) to walk
// the DAG of the contexts (for example subpasses)
template <typename Detector>
@@ -3521,13 +3530,13 @@
write_dependency_chain = read_execution_barriers;
}
-bool ResourceAccessState::QueueTagPredicate::operator()(QueueId usage_queue, ResourceUsageTag usage_tag) {
- return (queue == usage_queue) && (tag <= usage_tag);
+bool ResourceAccessState::QueueTagPredicate::operator()(QueueId usage_queue, ResourceUsageTag usage_tag) const {
+ return (usage_queue == queue) && (usage_tag <= tag);
}
-bool ResourceAccessState::QueuePredicate::operator()(QueueId usage_queue, ResourceUsageTag) { return queue == usage_queue; }
+bool ResourceAccessState::QueuePredicate::operator()(QueueId usage_queue, ResourceUsageTag) const { return queue == usage_queue; }
-bool ResourceAccessState::TagPredicate::operator()(QueueId, ResourceUsageTag usage_tag) { return tag <= usage_tag; }
+bool ResourceAccessState::TagPredicate::operator()(QueueId, ResourceUsageTag usage_tag) const { return tag <= usage_tag; }
// Return if the resulting state is "empty"
template <typename Pred>
@@ -3777,6 +3786,36 @@
return reserve;
}
+void SyncValidator::ApplyTaggedWait(QueueId queue_id, ResourceUsageTag tag) {
+ // We need to go through every queue batch context and clear all accesses this wait synchronizes
+ // As usual -- two groups, the "last batch" and the signaled semaphores
+ // NOTE: Since ApplyTaggedWait crawls through every usage in every ResourceAccessState in the AccessContext of *every*
+ // QueueBatchContext, track which we've done to avoid duplicate traversals
+ QueueBatchContext::BatchSet queue_batch_contexts = GetQueueBatchSnapshot();
+ for (auto &batch : queue_batch_contexts) {
+ batch->ApplyTaggedWait(queue_id, tag);
+ }
+}
+
+void SyncValidator::UpdateFenceWaitInfo(VkFence fence, QueueId queue_id, ResourceUsageTag tag) {
+ if (fence != VK_NULL_HANDLE) {
+ // Overwrite the current fence wait information
+ // NOTE: Not doing fence usage validation here, leaving that in CoreChecks intentionally
+ auto fence_state = Get<FENCE_STATE>(fence);
+ waitable_fences_[fence] = {fence_state, tag, queue_id};
+ }
+}
+
+void SyncValidator::WaitForFence(VkFence fence) {
+ auto fence_it = waitable_fences_.find(fence);
+ if (fence_it != waitable_fences_.end()) {
+ // The fence may no longer be waitable for several valid reasons.
+ FenceSyncState &wait_for = fence_it->second;
+ ApplyTaggedWait(wait_for.queue_id, wait_for.tag);
+ waitable_fences_.erase(fence_it);
+ }
+}
+
const QueueSyncState *SyncValidator::GetQueueSyncState(VkQueue queue) const {
return GetMappedPlainFromShared(queue_sync_states_, queue);
}
@@ -7161,17 +7200,10 @@
const auto queue_state = GetQueueSyncStateShared(queue);
if (!queue_state) return; // Invalid queue
QueueId waited_queue = queue_state->GetQueueId();
+ ApplyTaggedWait(waited_queue, ResourceUsageRecord::kMaxIndex);
- // We need to go through every queue batch context and clear all accesses this wait synchronizes
- // As usual -- two groups, the "last batch" and the signaled semaphores
- // NOTE: Since ApplyTaggedWait crawls through every usage in every ResourceAccessState in the AccessContext of *every*
- // QueueBatchContext, track which we've done to avoid duplicate traversals
- QueueBatchContext::BatchSet queue_batch_contexts = GetQueueBatchSnapshot();
- for (auto &batch : queue_batch_contexts) {
- batch->ApplyTaggedWait(waited_queue, ResourceUsageRecord::kMaxIndex);
- }
-
- // TODO: Fences affected by Wait
+ // Eliminate waitable fences from the current queue.
+ layer_data::EraseIf(waitable_fences_, [waited_queue](const SignaledFence &sf) { return sf.second.queue_id == waited_queue; });
}
void SyncValidator::PostCallRecordDeviceWaitIdle(VkDevice device, VkResult result) {
@@ -7182,7 +7214,8 @@
batch->ApplyDeviceWait();
}
- // TODO: Update Fences affected by Wait
+ // As we we've waited for everything on device, any waits are mooted.
+ waitable_fences_.clear();
}
struct QueueSubmitCmdState {
@@ -7308,8 +7341,8 @@
// Update the global access log from the one built during validation
global_access_log_.MergeMove(std::move(cmd_state->logger));
-
- // WIP: record information about fences
+ ResourceUsageRange fence_tag_range = ReserveGlobalTagRange(1U);
+ UpdateFenceWaitInfo(fence, queue_state->GetQueueId(), fence_tag_range.begin);
}
bool SyncValidator::PreCallValidateQueueSubmit2KHR(VkQueue queue, uint32_t submitCount, const VkSubmitInfo2KHR *pSubmits,
@@ -7331,6 +7364,27 @@
// WIP: Add Submit2 support
}
+void SyncValidator::PostCallRecordGetFenceStatus(VkDevice device, VkFence fence, VkResult result) {
+ StateTracker::PostCallRecordGetFenceStatus(device, fence, result);
+ if (!enabled[sync_validation_queue_submit]) return;
+ if (result == VK_SUCCESS) {
+ // fence is signalled, mark it as waited for
+ WaitForFence(fence);
+ }
+}
+
+void SyncValidator::PostCallRecordWaitForFences(VkDevice device, uint32_t fenceCount, const VkFence *pFences, VkBool32 waitAll,
+ uint64_t timeout, VkResult result) {
+ StateTracker::PostCallRecordWaitForFences(device, fenceCount, pFences, waitAll, timeout, result);
+ if (!enabled[sync_validation_queue_submit]) return;
+ if ((result == VK_SUCCESS) && ((VK_TRUE == waitAll) || (1 == fenceCount))) {
+ // We can only know the pFences have signal if we waited for all of them, or there was only one of them
+ for (uint32_t i = 0; i < fenceCount; i++) {
+ WaitForFence(pFences[i]);
+ }
+ }
+}
+
AttachmentViewGen::AttachmentViewGen(const IMAGE_VIEW_STATE *view, const VkOffset3D &offset, const VkExtent3D &extent)
: view_(view), view_mask_(), gen_store_() {
if (!view_ || !view_->image_state || !SimpleBinding(*view_->image_state)) return;
@@ -7455,16 +7509,11 @@
VulkanTypedHandle QueueBatchContext::Handle() const { return queue_state_->Handle(); }
void QueueBatchContext::ApplyTaggedWait(QueueId queue_id, ResourceUsageTag tag) {
- QueueWaitWorm wait_worm(queue_id);
- access_context_.ForAll(wait_worm);
- if (wait_worm.erase_all) {
- access_context_.Reset();
- } else {
- // TODO: Profiling will tell us if we need a more efficient clean up.
- for (const auto &address : wait_worm.erase_list) {
- access_context_.DeleteAccess(address);
- }
- }
+ ResourceAccessState::QueueTagPredicate predicate{queue_id, tag};
+ access_context_.EraseIf([&predicate](ResourceAccessRangeMap::value_type &access) {
+ // Apply..Wait returns true if the waited access is empty...
+ return access.second.ApplyQueueTagWait(predicate);
+ });
if (queue_id == GetQueueId()) {
events_context_.ApplyTaggedWait(GetQueueFlags(), tag);
@@ -7690,17 +7739,6 @@
access_context_.SetStartTag(bias);
}
-QueueBatchContext::QueueWaitWorm::QueueWaitWorm(QueueId queue, ResourceUsageTag tag) : predicate(queue) {}
-
-void QueueBatchContext::QueueWaitWorm::operator()(AccessAddressType address_type, ResourceAccessRangeMap::value_type &access) {
- bool erased = access.second.ApplyQueueTagWait(predicate);
- if (erased) {
- erase_list.emplace_back(address_type, access.first);
- } else {
- erase_all = false;
- }
-}
-
AccessLogger::BatchLog *AccessLogger::AddBatch(const QueueSyncState *queue_state, uint64_t submit_id, uint32_t batch_id,
const ResourceUsageRange &range) {
const auto inserted = access_log_map_.insert(std::make_pair(range, BatchLog(BatchRecord(queue_state, submit_id, batch_id))));
@@ -7782,3 +7820,5 @@
assert(batch);
assert(sem_state);
}
+
+FenceSyncState::FenceSyncState() : fence(), tag(kInvalidTag), queue_id(QueueSyncState::kQueueIdInvalid) {}