File: fence_state.cpp

package info (click to toggle)
vulkan-validationlayers 1.4.321.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 47,412 kB
  • sloc: cpp: 594,175; python: 11,321; sh: 24; makefile: 20; xml: 14
file content (203 lines) | stat: -rw-r--r-- 8,266 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
/* Copyright (c) 2015-2025 The Khronos Group Inc.
 * Copyright (c) 2015-2025 Valve Corporation
 * Copyright (c) 2015-2025 LunarG, Inc.
 * Copyright (C) 2015-2024 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.
 */
#include "state_tracker/fence_state.h"
#include "state_tracker/queue_state.h"
#include "state_tracker/state_tracker.h"

static VkExternalFenceHandleTypeFlags GetExportHandleTypes(const VkFenceCreateInfo *info) {
    auto export_info = vku::FindStructInPNextChain<VkExportFenceCreateInfo>(info->pNext);
    return export_info ? export_info->handleTypes : 0;
}

vvl::Fence::Fence(Logger &logger, VkFence handle, const VkFenceCreateInfo *pCreateInfo)
    : RefcountedStateObject(handle, kVulkanObjectTypeFence),
      flags(pCreateInfo->flags),
      export_handle_types(GetExportHandleTypes(pCreateInfo)),
      state_((pCreateInfo->flags & VK_FENCE_CREATE_SIGNALED_BIT) ? kRetired : kUnsignaled),
      completed_(),
      waiter_(completed_.get_future()),
      logger_(logger) {}

const VulkanTypedHandle *vvl::Fence::InUse() const {
    auto guard = ReadLock();
    // Fence does not have a parent (in the sense of a VVL state object), and the value returned
    // by the base class InUse is not useful for reporting (it is the fence's own handle)
    const bool in_use = RefcountedStateObject::InUse() != nullptr;
    if (!in_use) {
        return nullptr;
    }
    // If the fence is in-use there should be a queue that uses it.
    // NOTE: in-use checks are always with regard to queue operations.
    assert(queue_ != nullptr && "Can't find queue that uses the fence");
    if (queue_) {
        return &queue_->Handle();
    }
    static const VulkanTypedHandle empty{};
    return &empty;
}

bool vvl::Fence::EnqueueSignal(vvl::Queue *queue_state, uint64_t next_seq) {
    auto guard = WriteLock();
    if (scope_ != kInternal) {
        return true;
    }
    // Mark fence in use
    state_ = kInflight;
    queue_ = queue_state;
    seq_ = next_seq;
    return false;
}

// Called from a non-queue operation, such as vkWaitForFences()|
void vvl::Fence::NotifyAndWait(const Location &loc) {
    std::shared_future<void> waiter;
    std::optional<SubmissionReference> present_submission_ref;
    {
        // Hold the lock only while updating members, but not
        // while waiting
        auto guard = WriteLock();
        if (state_ == kInflight) {
            if (queue_) {
                queue_->Notify(seq_);
                waiter = waiter_;
            } else {
                state_ = kRetired;
                completed_.set_value();
                queue_ = nullptr;
                seq_ = 0;
            }
            present_submission_ref = std::move(present_submission_ref_);
            present_submission_ref_.reset();
        }
        // Cleanup wait semaphores.
        // NOTE: Functions like QueueWaitIdle put fence in the retired state, still it can have
        // the list of present semaphores, which are not cleared by QueueWaitIdle when swapchain
        // maintenance extension is enabled. That's the reason this code is not under kInflight condition.
        for (auto &semaphore : present_wait_semaphores_) {
            semaphore->ClearSwapchainWaitInfo();
        }
        present_wait_semaphores_.clear();
    }
    if (waiter.valid()) {
        auto result = waiter.wait_until(GetCondWaitTimeout());
        if (result != std::future_status::ready) {
            logger_.LogError("INTERNAL-ERROR-VkFence-state-timeout", Handle(), loc,
                             "The Validation Layers hit a timeout waiting for fence state to update.");
        }
    }
    if (present_submission_ref.has_value()) {
        present_submission_ref->queue->NotifyAndWait(loc, present_submission_ref->seq);
    }
}

// Retire from a queue operation
void vvl::Fence::Retire() {
    auto guard = WriteLock();
    if (state_ == kInflight) {
        state_ = kRetired;
        completed_.set_value();
        queue_ = nullptr;
        seq_ = 0;
    }
}

void vvl::Fence::Reset() {
    auto guard = WriteLock();
    queue_ = nullptr;
    seq_ = 0;
    // spec: If any member of pFences currently has its payload imported with temporary permanence,
    // that fence’s prior permanent payload is first restored. The remaining operations described
    // therefore operate on the restored payload.
    if (scope_ == kExternalTemporary) {
        scope_ = kInternal;
        imported_handle_type_.reset();
    }
    state_ = kUnsignaled;
    completed_ = std::promise<void>();
    waiter_ = std::shared_future<void>(completed_.get_future());
    present_submission_ref_.reset();

    // Do not reset swapchain-in-use state of each semaphore here, only stop the tracking.
    // In order to reset swapchain-in-use state we need to wait on the fence.
    present_wait_semaphores_.clear();
}

void vvl::Fence::Import(VkExternalFenceHandleTypeFlagBits handle_type, VkFenceImportFlags flags) {
    auto guard = WriteLock();
    if (scope_ != kExternalPermanent) {
        if (handle_type != VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT && (flags & VK_FENCE_IMPORT_TEMPORARY_BIT) == 0) {
            scope_ = kExternalPermanent;
        } else if (scope_ == kInternal) {
            scope_ = kExternalTemporary;
        }
    }
    imported_handle_type_ = handle_type;
}

void vvl::Fence::Export(VkExternalFenceHandleTypeFlagBits handle_type) {
    auto guard = WriteLock();
    if (handle_type != VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT) {
        // Export with reference transference becomes external
        scope_ = kExternalPermanent;
    } else {
        // Export with copy transference has a side effect of resetting the fence
        if (scope_ == kExternalTemporary) {
            scope_ = kInternal;
            imported_handle_type_.reset();
        }
        state_ = kUnsignaled;
        completed_ = std::promise<void>();
        waiter_ = std::shared_future<void>(completed_.get_future());
    }
}

std::optional<VkExternalFenceHandleTypeFlagBits> vvl::Fence::ImportedHandleType() const {
    auto guard = ReadLock();

    // Sanity check: fence imported -> scope is not internal
    assert(!imported_handle_type_.has_value() || scope_ != kInternal);

    return imported_handle_type_;
}

void vvl::Fence::SetPresentSubmissionRef(const SubmissionReference &present_submission_ref) {
    auto guard = WriteLock();
    // In an error-free scenario, "present_submission_ref_" member has no value (as optional).
    // If the fence is reused without being waited on/reset (which causes a validation error),
    // then we may find a stale ref value here. Simply overwrite it with a new ref.
    //
    // VVL INSIGHT: Interestingly, stale ref value can't happen in VVL tests because this function
    // (from the Record phase) is not called if validation fails. However, the Record phase is
    // always called for regular applications even in the case of validation errors.
    // This means we cannot assert that submission ref member is empty. Such an assert would be
    // valid for VVL tests but not for regular apps. It's not a big deal, but this observation
    // may be useful to get better understanding of VVL internals.
    //
    // assert(!present_submission_ref_.has_value()); - only valid for VVL tests

    assert(present_submission_ref.queue != nullptr);
    present_submission_ref_ = present_submission_ref;
}

void vvl::Fence::SetPresentWaitSemaphores(vvl::span<std::shared_ptr<vvl::Semaphore>> present_wait_semaphores) {
    present_wait_semaphores_.clear();
    for (const auto &semaphore : present_wait_semaphores) {
        present_wait_semaphores_.emplace_back(semaphore);
    }
}