Jeremy Gebben | 4af0aa8 | 2021-09-08 09:35:16 -0600 | [diff] [blame^] | 1 | /* Copyright (c) 2015-2021 The Khronos Group Inc. |
| 2 | * Copyright (c) 2015-2021 Valve Corporation |
| 3 | * Copyright (c) 2015-2021 LunarG, Inc. |
| 4 | * Copyright (C) 2015-2021 Google Inc. |
| 5 | * Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. |
| 6 | * |
| 7 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 8 | * you may not use this file except in compliance with the License. |
| 9 | * You may obtain a copy of the License at |
| 10 | * |
| 11 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 12 | * |
| 13 | * Unless required by applicable law or agreed to in writing, software |
| 14 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 16 | * See the License for the specific language governing permissions and |
| 17 | * limitations under the License. |
| 18 | * |
| 19 | * Author: Courtney Goeltzenleuchter <courtneygo@google.com> |
| 20 | * Author: Tobin Ehlis <tobine@google.com> |
| 21 | * Author: Chris Forbes <chrisf@ijw.co.nz> |
| 22 | * Author: Mark Lobodzinski <mark@lunarg.com> |
| 23 | * Author: Dave Houlton <daveh@lunarg.com> |
| 24 | * Author: John Zulauf <jzulauf@lunarg.com> |
| 25 | * Author: Tobias Hector <tobias.hector@amd.com> |
| 26 | */ |
| 27 | #include "queue_state.h" |
| 28 | #include "state_tracker.h" |
| 29 | #include "cmd_buffer_state.h" |
| 30 | |
| 31 | void ValidationStateTracker::RecordSubmitWaitSemaphore(CB_SUBMISSION &submission, VkQueue queue, VkSemaphore semaphore, |
| 32 | uint64_t value, uint64_t next_seq) { |
| 33 | auto semaphore_state = GetSemaphoreState(semaphore); |
| 34 | if (semaphore_state) { |
| 35 | if (semaphore_state->scope == kSyncScopeInternal) { |
| 36 | SEMAPHORE_WAIT wait; |
| 37 | wait.semaphore = semaphore; |
| 38 | wait.type = semaphore_state->type; |
| 39 | if (semaphore_state->type == VK_SEMAPHORE_TYPE_BINARY) { |
| 40 | if (semaphore_state->signaler.first != VK_NULL_HANDLE) { |
| 41 | wait.queue = semaphore_state->signaler.first; |
| 42 | wait.seq = semaphore_state->signaler.second; |
| 43 | submission.waitSemaphores.emplace_back(std::move(wait)); |
| 44 | semaphore_state->BeginUse(); |
| 45 | } |
| 46 | semaphore_state->signaler.first = VK_NULL_HANDLE; |
| 47 | semaphore_state->signaled = false; |
| 48 | } else if (semaphore_state->payload < value) { |
| 49 | wait.queue = queue; |
| 50 | wait.seq = next_seq; |
| 51 | wait.payload = value; |
| 52 | submission.waitSemaphores.emplace_back(std::move(wait)); |
| 53 | semaphore_state->BeginUse(); |
| 54 | } |
| 55 | } else { |
| 56 | submission.externalSemaphores.push_back(semaphore); |
| 57 | semaphore_state->BeginUse(); |
| 58 | if (semaphore_state->scope == kSyncScopeExternalTemporary) { |
| 59 | semaphore_state->scope = kSyncScopeInternal; |
| 60 | } |
| 61 | } |
| 62 | } |
| 63 | } |
| 64 | |
| 65 | bool ValidationStateTracker::RecordSubmitSignalSemaphore(CB_SUBMISSION &submission, VkQueue queue, VkSemaphore semaphore, |
| 66 | uint64_t value, uint64_t next_seq) { |
| 67 | bool retire_early = false; |
| 68 | auto semaphore_state = GetSemaphoreState(semaphore); |
| 69 | if (semaphore_state) { |
| 70 | if (semaphore_state->scope == kSyncScopeInternal) { |
| 71 | SEMAPHORE_SIGNAL signal; |
| 72 | signal.semaphore = semaphore; |
| 73 | signal.seq = next_seq; |
| 74 | if (semaphore_state->type == VK_SEMAPHORE_TYPE_BINARY) { |
| 75 | semaphore_state->signaler.first = queue; |
| 76 | semaphore_state->signaler.second = next_seq; |
| 77 | semaphore_state->signaled = true; |
| 78 | } else { |
| 79 | signal.payload = value; |
| 80 | } |
| 81 | semaphore_state->BeginUse(); |
| 82 | submission.signalSemaphores.emplace_back(std::move(signal)); |
| 83 | } else { |
| 84 | // Retire work up until this submit early, we will not see the wait that corresponds to this signal |
| 85 | retire_early = true; |
| 86 | } |
| 87 | } |
| 88 | return retire_early; |
| 89 | } |
| 90 | |
| 91 | // Submit a fence to a queue, delimiting previous fences and previous untracked |
| 92 | // work by it. |
| 93 | static void SubmitFence(QUEUE_STATE *pQueue, FENCE_STATE *pFence, uint64_t submitCount) { |
| 94 | pFence->state = FENCE_INFLIGHT; |
| 95 | pFence->signaler.first = pQueue->Queue(); |
| 96 | pFence->signaler.second = pQueue->seq + pQueue->submissions.size() + submitCount; |
| 97 | } |
| 98 | |
| 99 | uint64_t ValidationStateTracker::RecordSubmitFence(QUEUE_STATE *queue_state, VkFence fence, uint32_t submit_count) { |
| 100 | auto fence_state = GetFenceState(fence); |
| 101 | uint64_t early_retire_seq = 0; |
| 102 | if (fence_state) { |
| 103 | if (fence_state->scope == kSyncScopeInternal) { |
| 104 | // Mark fence in use |
| 105 | SubmitFence(queue_state, fence_state, std::max(1u, submit_count)); |
| 106 | if (!submit_count) { |
| 107 | // If no submissions, but just dropping a fence on the end of the queue, |
| 108 | // record an empty submission with just the fence, so we can determine |
| 109 | // its completion. |
| 110 | CB_SUBMISSION submission; |
| 111 | submission.fence = fence; |
| 112 | queue_state->submissions.emplace_back(std::move(submission)); |
| 113 | } |
| 114 | } else { |
| 115 | // Retire work up until this fence early, we will not see the wait that corresponds to this signal |
| 116 | early_retire_seq = queue_state->seq + queue_state->submissions.size(); |
| 117 | } |
| 118 | } |
| 119 | return early_retire_seq; |
| 120 | } |
| 121 | |
| 122 | void ValidationStateTracker::RetireWorkOnQueue(QUEUE_STATE *pQueue, uint64_t seq) { |
| 123 | layer_data::unordered_map<VkQueue, uint64_t> other_queue_seqs; |
| 124 | layer_data::unordered_map<VkSemaphore, uint64_t> timeline_semaphore_counters; |
| 125 | |
| 126 | // Roll this queue forward, one submission at a time. |
| 127 | while (pQueue->seq < seq) { |
| 128 | auto &submission = pQueue->submissions.front(); |
| 129 | |
| 130 | for (auto &wait : submission.waitSemaphores) { |
| 131 | auto semaphore_state = GetSemaphoreState(wait.semaphore); |
| 132 | if (semaphore_state) { |
| 133 | semaphore_state->EndUse(); |
| 134 | } |
| 135 | if (wait.type == VK_SEMAPHORE_TYPE_TIMELINE) { |
| 136 | auto &last_counter = timeline_semaphore_counters[wait.semaphore]; |
| 137 | last_counter = std::max(last_counter, wait.payload); |
| 138 | } else { |
| 139 | auto &last_seq = other_queue_seqs[wait.queue]; |
| 140 | last_seq = std::max(last_seq, wait.seq); |
| 141 | } |
| 142 | } |
| 143 | |
| 144 | for (auto &signal : submission.signalSemaphores) { |
| 145 | auto semaphore_state = GetSemaphoreState(signal.semaphore); |
| 146 | if (semaphore_state) { |
| 147 | semaphore_state->EndUse(); |
| 148 | if (semaphore_state->type == VK_SEMAPHORE_TYPE_TIMELINE && semaphore_state->payload < signal.payload) { |
| 149 | semaphore_state->payload = signal.payload; |
| 150 | } |
| 151 | } |
| 152 | } |
| 153 | |
| 154 | for (auto &semaphore : submission.externalSemaphores) { |
| 155 | auto semaphore_state = GetSemaphoreState(semaphore); |
| 156 | if (semaphore_state) { |
| 157 | semaphore_state->EndUse(); |
| 158 | } |
| 159 | } |
| 160 | |
| 161 | for (auto cb : submission.cbs) { |
| 162 | auto cb_node = Get<CMD_BUFFER_STATE>(cb); |
| 163 | if (!cb_node) { |
| 164 | continue; |
| 165 | } |
| 166 | // First perform decrement on general case bound objects |
| 167 | for (auto event : cb_node->writeEventsBeforeWait) { |
| 168 | auto event_node = eventMap.find(event); |
| 169 | if (event_node != eventMap.end()) { |
| 170 | event_node->second->write_in_use--; |
| 171 | } |
| 172 | } |
| 173 | QueryMap local_query_to_state_map; |
| 174 | VkQueryPool first_pool = VK_NULL_HANDLE; |
| 175 | for (auto &function : cb_node->queryUpdates) { |
| 176 | function(nullptr, /*do_validate*/ false, first_pool, submission.perf_submit_pass, &local_query_to_state_map); |
| 177 | } |
| 178 | |
| 179 | for (const auto &query_state_pair : local_query_to_state_map) { |
| 180 | if (query_state_pair.second == QUERYSTATE_ENDED) { |
| 181 | queryToStateMap[query_state_pair.first] = QUERYSTATE_AVAILABLE; |
| 182 | } |
| 183 | } |
| 184 | if (cb_node->createInfo.level == VK_COMMAND_BUFFER_LEVEL_PRIMARY) { |
| 185 | cb_node->EndUse(); |
| 186 | } |
| 187 | } |
| 188 | |
| 189 | auto fence_state = GetFenceState(submission.fence); |
| 190 | if (fence_state && fence_state->scope == kSyncScopeInternal) { |
| 191 | fence_state->state = FENCE_RETIRED; |
| 192 | } |
| 193 | |
| 194 | pQueue->submissions.pop_front(); |
| 195 | pQueue->seq++; |
| 196 | } |
| 197 | |
| 198 | // Roll other queues forward to the highest seq we saw a wait for |
| 199 | for (const auto &qs : other_queue_seqs) { |
| 200 | RetireWorkOnQueue(GetQueueState(qs.first), qs.second); |
| 201 | } |
| 202 | for (const auto &sc : timeline_semaphore_counters) { |
| 203 | RetireTimelineSemaphore(sc.first, sc.second); |
| 204 | } |
| 205 | } |
| 206 | |
| 207 | void ValidationStateTracker::RetireFence(VkFence fence) { |
| 208 | auto fence_state = GetFenceState(fence); |
| 209 | if (fence_state && fence_state->scope == kSyncScopeInternal) { |
| 210 | if (fence_state->signaler.first != VK_NULL_HANDLE) { |
| 211 | // Fence signaller is a queue -- use this as proof that prior operations on that queue have completed. |
| 212 | RetireWorkOnQueue(GetQueueState(fence_state->signaler.first), fence_state->signaler.second); |
| 213 | } else { |
| 214 | // Fence signaller is the WSI. We're not tracking what the WSI op actually /was/ in CV yet, but we need to mark |
| 215 | // the fence as retired. |
| 216 | fence_state->state = FENCE_RETIRED; |
| 217 | } |
| 218 | } |
| 219 | } |
| 220 | |
| 221 | void ValidationStateTracker::RetireTimelineSemaphore(VkSemaphore semaphore, uint64_t until_payload) { |
| 222 | auto semaphore_state = GetSemaphoreState(semaphore); |
| 223 | if (semaphore_state) { |
| 224 | for (const auto &pair : queueMap) { |
| 225 | const auto &queue_state = pair.second; |
| 226 | uint64_t max_seq = 0; |
| 227 | for (const auto &submission : queue_state->submissions) { |
| 228 | for (const auto &signal_semaphore : submission.signalSemaphores) { |
| 229 | if (signal_semaphore.semaphore == semaphore && signal_semaphore.payload <= until_payload) { |
| 230 | if (signal_semaphore.seq > max_seq) { |
| 231 | max_seq = signal_semaphore.seq; |
| 232 | } |
| 233 | } |
| 234 | } |
| 235 | } |
| 236 | if (max_seq) { |
| 237 | RetireWorkOnQueue(queue_state.get(), max_seq); |
| 238 | } |
| 239 | } |
| 240 | } |
| 241 | } |