blob: 1d54029672d0d87e5c750fa1a06dbbfcfae851df [file] [log] [blame]
/* Copyright (c) 2015-2021 The Khronos Group Inc.
* Copyright (c) 2015-2021 Valve Corporation
* Copyright (c) 2015-2021 LunarG, Inc.
* Copyright (C) 2015-2021 Google Inc.
* Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Author: Courtney Goeltzenleuchter <courtneygo@google.com>
* Author: Tobin Ehlis <tobine@google.com>
* Author: Chris Forbes <chrisf@ijw.co.nz>
* Author: Mark Lobodzinski <mark@lunarg.com>
* Author: Dave Houlton <daveh@lunarg.com>
* Author: John Zulauf <jzulauf@lunarg.com>
* Author: Tobias Hector <tobias.hector@amd.com>
*/
#include "queue_state.h"
#include "state_tracker.h"
#include "cmd_buffer_state.h"
void ValidationStateTracker::RecordSubmitWaitSemaphore(CB_SUBMISSION &submission, VkQueue queue, VkSemaphore semaphore,
uint64_t value, uint64_t next_seq) {
auto semaphore_state = GetSemaphoreState(semaphore);
if (semaphore_state) {
if (semaphore_state->scope == kSyncScopeInternal) {
SEMAPHORE_WAIT wait;
wait.semaphore = semaphore;
wait.type = semaphore_state->type;
if (semaphore_state->type == VK_SEMAPHORE_TYPE_BINARY) {
if (semaphore_state->signaler.first != VK_NULL_HANDLE) {
wait.queue = semaphore_state->signaler.first;
wait.seq = semaphore_state->signaler.second;
submission.waitSemaphores.emplace_back(std::move(wait));
semaphore_state->BeginUse();
}
semaphore_state->signaler.first = VK_NULL_HANDLE;
semaphore_state->signaled = false;
} else if (semaphore_state->payload < value) {
wait.queue = queue;
wait.seq = next_seq;
wait.payload = value;
submission.waitSemaphores.emplace_back(std::move(wait));
semaphore_state->BeginUse();
}
} else {
submission.externalSemaphores.push_back(semaphore);
semaphore_state->BeginUse();
if (semaphore_state->scope == kSyncScopeExternalTemporary) {
semaphore_state->scope = kSyncScopeInternal;
}
}
}
}
bool ValidationStateTracker::RecordSubmitSignalSemaphore(CB_SUBMISSION &submission, VkQueue queue, VkSemaphore semaphore,
uint64_t value, uint64_t next_seq) {
bool retire_early = false;
auto semaphore_state = GetSemaphoreState(semaphore);
if (semaphore_state) {
if (semaphore_state->scope == kSyncScopeInternal) {
SEMAPHORE_SIGNAL signal;
signal.semaphore = semaphore;
signal.seq = next_seq;
if (semaphore_state->type == VK_SEMAPHORE_TYPE_BINARY) {
semaphore_state->signaler.first = queue;
semaphore_state->signaler.second = next_seq;
semaphore_state->signaled = true;
} else {
signal.payload = value;
}
semaphore_state->BeginUse();
submission.signalSemaphores.emplace_back(std::move(signal));
} else {
// Retire work up until this submit early, we will not see the wait that corresponds to this signal
retire_early = true;
}
}
return retire_early;
}
// Submit a fence to a queue, delimiting previous fences and previous untracked
// work by it.
static void SubmitFence(QUEUE_STATE *pQueue, FENCE_STATE *pFence, uint64_t submitCount) {
pFence->state = FENCE_INFLIGHT;
pFence->signaler.first = pQueue->Queue();
pFence->signaler.second = pQueue->seq + pQueue->submissions.size() + submitCount;
}
uint64_t ValidationStateTracker::RecordSubmitFence(QUEUE_STATE *queue_state, VkFence fence, uint32_t submit_count) {
auto fence_state = GetFenceState(fence);
uint64_t early_retire_seq = 0;
if (fence_state) {
if (fence_state->scope == kSyncScopeInternal) {
// Mark fence in use
SubmitFence(queue_state, fence_state, std::max(1u, submit_count));
if (!submit_count) {
// 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.
CB_SUBMISSION submission;
submission.fence = fence;
queue_state->submissions.emplace_back(std::move(submission));
}
} else {
// Retire work up until this fence early, we will not see the wait that corresponds to this signal
early_retire_seq = queue_state->seq + queue_state->submissions.size();
}
}
return early_retire_seq;
}
void ValidationStateTracker::RetireWorkOnQueue(QUEUE_STATE *pQueue, uint64_t seq) {
layer_data::unordered_map<VkQueue, uint64_t> other_queue_seqs;
layer_data::unordered_map<VkSemaphore, uint64_t> timeline_semaphore_counters;
// Roll this queue forward, one submission at a time.
while (pQueue->seq < seq) {
auto &submission = pQueue->submissions.front();
for (auto &wait : submission.waitSemaphores) {
auto semaphore_state = GetSemaphoreState(wait.semaphore);
if (semaphore_state) {
semaphore_state->EndUse();
}
if (wait.type == VK_SEMAPHORE_TYPE_TIMELINE) {
auto &last_counter = timeline_semaphore_counters[wait.semaphore];
last_counter = std::max(last_counter, wait.payload);
} else {
auto &last_seq = other_queue_seqs[wait.queue];
last_seq = std::max(last_seq, wait.seq);
}
}
for (auto &signal : submission.signalSemaphores) {
auto semaphore_state = GetSemaphoreState(signal.semaphore);
if (semaphore_state) {
semaphore_state->EndUse();
if (semaphore_state->type == VK_SEMAPHORE_TYPE_TIMELINE && semaphore_state->payload < signal.payload) {
semaphore_state->payload = signal.payload;
}
}
}
for (auto &semaphore : submission.externalSemaphores) {
auto semaphore_state = GetSemaphoreState(semaphore);
if (semaphore_state) {
semaphore_state->EndUse();
}
}
for (auto cb : submission.cbs) {
auto cb_node = Get<CMD_BUFFER_STATE>(cb);
if (!cb_node) {
continue;
}
// First perform decrement on general case bound objects
for (auto event : cb_node->writeEventsBeforeWait) {
auto event_node = eventMap.find(event);
if (event_node != eventMap.end()) {
event_node->second->write_in_use--;
}
}
QueryMap local_query_to_state_map;
VkQueryPool first_pool = VK_NULL_HANDLE;
for (auto &function : cb_node->queryUpdates) {
function(nullptr, /*do_validate*/ false, first_pool, submission.perf_submit_pass, &local_query_to_state_map);
}
for (const auto &query_state_pair : local_query_to_state_map) {
if (query_state_pair.second == QUERYSTATE_ENDED) {
queryToStateMap[query_state_pair.first] = QUERYSTATE_AVAILABLE;
}
}
if (cb_node->createInfo.level == VK_COMMAND_BUFFER_LEVEL_PRIMARY) {
cb_node->EndUse();
}
}
auto fence_state = GetFenceState(submission.fence);
if (fence_state && fence_state->scope == kSyncScopeInternal) {
fence_state->state = FENCE_RETIRED;
}
pQueue->submissions.pop_front();
pQueue->seq++;
}
// Roll other queues forward to the highest seq we saw a wait for
for (const auto &qs : other_queue_seqs) {
RetireWorkOnQueue(GetQueueState(qs.first), qs.second);
}
for (const auto &sc : timeline_semaphore_counters) {
RetireTimelineSemaphore(sc.first, sc.second);
}
}
void ValidationStateTracker::RetireFence(VkFence fence) {
auto fence_state = GetFenceState(fence);
if (fence_state && fence_state->scope == kSyncScopeInternal) {
if (fence_state->signaler.first != VK_NULL_HANDLE) {
// Fence signaller is a queue -- use this as proof that prior operations on that queue have completed.
RetireWorkOnQueue(GetQueueState(fence_state->signaler.first), fence_state->signaler.second);
} else {
// Fence signaller is the WSI. We're not tracking what the WSI op actually /was/ in CV yet, but we need to mark
// the fence as retired.
fence_state->state = FENCE_RETIRED;
}
}
}
void ValidationStateTracker::RetireTimelineSemaphore(VkSemaphore semaphore, uint64_t until_payload) {
auto semaphore_state = GetSemaphoreState(semaphore);
if (semaphore_state) {
for (const auto &pair : queueMap) {
const auto &queue_state = pair.second;
uint64_t max_seq = 0;
for (const auto &submission : queue_state->submissions) {
for (const auto &signal_semaphore : submission.signalSemaphores) {
if (signal_semaphore.semaphore == semaphore && signal_semaphore.payload <= until_payload) {
if (signal_semaphore.seq > max_seq) {
max_seq = signal_semaphore.seq;
}
}
}
}
if (max_seq) {
RetireWorkOnQueue(queue_state.get(), max_seq);
}
}
}
}