File: sync_submit.h

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 (421 lines) | stat: -rw-r--r-- 19,239 bytes parent folder | download | duplicates (6)
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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
/*
 * Copyright (c) 2019-2025 Valve Corporation
 * Copyright (c) 2019-2025 LunarG, Inc.
 *
 * 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.
 */

#pragma once
#include "sync/sync_commandbuffer.h"
#include "state_tracker/queue_state.h"

struct PresentedImage;
class QueueBatchContext;
struct QueueSubmitCmdState;
class QueueSyncState;
class SyncValidator;

using BatchContextPtr = std::shared_ptr<QueueBatchContext>;
using BatchContextConstPtr = std::shared_ptr<const QueueBatchContext>;

namespace vvl {
class CommandBuffer;
class Semaphore;
}  // namespace vvl

using CommandBufferConstPtr = std::shared_ptr<const vvl::CommandBuffer>;

struct AcquiredImage {
    std::shared_ptr<const vvl::Image> image;
    subresource_adapter::ImageRangeGenerator generator;
    ResourceUsageTag present_tag;
    ResourceUsageTag acquire_tag;
    bool Invalid() const;

    AcquiredImage() = default;
    AcquiredImage(const PresentedImage &presented, ResourceUsageTag acq_tag);
};

// Information associated with a semaphore signal
struct SignalInfo {
    // QueueSubmit signal
    SignalInfo(const std::shared_ptr<const vvl::Semaphore> &semaphore_state, const std::shared_ptr<QueueBatchContext> &batch,
               const SyncExecScope &exec_scope, uint64_t timeline_value);

    // SignalSemaphore signal
    SignalInfo(const std::shared_ptr<const vvl::Semaphore> &semaphore_state, uint64_t timeline_value);

    // AcquireNextImage signal
    SignalInfo(const std::shared_ptr<const vvl::Semaphore> &semaphore_state, const PresentedImage &presented,
               ResourceUsageTag acquire_tag);

    // Signaled semaphore. Not null.
    std::shared_ptr<const vvl::Semaphore> semaphore_state;

    // Batch from the signal's first scope. It is null for a host signal (vkSignalSemaphore)
    std::shared_ptr<QueueBatchContext> batch;

    // Use the first_scope.valid_accesses for the first access scope of non-host signals.
    // first_scope.queue is kQueueIdInvalid for a host signal (vkSignalSemaphore)
    SemaphoreScope first_scope;

    // Value signaled by a timeline semaphore
    uint64_t timeline_value = 0;

    // Swapchain specific signal info.
    // Batch field is the batch of the last present for the acquired image.
    // The AcquiredImage further limits the scope of the resolve operation, and the "barrier" will also
    // be special case (updating "PRESENTED" write with "ACQUIRE" read, as well as setting the barrier).
    //
    // NOTE: shared_ptr is used here as a memory saver. AcquiredImage is 224 bytes at the time
    // of writing and is not used in the queue submit signals. If we optimize ImageRangeGenerator
    // memory usage then shared_ptr can be replaced by std::optional to avoid allocation.
    std::shared_ptr<AcquiredImage> acquired_image;
};

// When the timeline wait is resolved, the previous signals can be removed
struct RemoveTimelineSignalsRequest {
    VkSemaphore semaphore = VK_NULL_HANDLE;

    // Remove all signals with a value *less than* this threshold (should not touch signals with equal value).
    uint64_t signal_threshold_value = 0;

    // NOTE: it's possible to have multiple queues with signals that match the wait value.
    // The specification defines that exactly one signal resolves the wait, and in the presence
    // of multiple such signals the implementation may choose any of them. It's the application
    // responsibility to be careful and not to create a race condition in such a scenario.
    //
    // The queue that signaled the resolving signal. Only the signals signaled by this queue should be processed.
    QueueId queue = 0;
};

// The requests to update SyncValidator's registry of binary/timeline signals.
// They are collected during validation phase and are applied in the record phase.
struct SignalsUpdate {
    vvl::unordered_map<VkSemaphore, SignalInfo> binary_signal_requests;
    vvl::unordered_set<VkSemaphore> binary_unsignal_requests;

    vvl::unordered_map<VkSemaphore, std::vector<SignalInfo>> timeline_signals;
    std::vector<RemoveTimelineSignalsRequest> remove_timeline_signals_requests;

    // Register submission batch signals.
    // Return true if at least one timeline signal was registered
    bool RegisterSignals(const BatchContextPtr &batch, const vvl::span<const VkSemaphoreSubmitInfo> &submit_signals);

    // Return resolving binary signal. Empty result in the case of a validation error
    std::optional<SignalInfo> OnBinaryWait(VkSemaphore semaphore);

    // Return resolving timeline signal. Empty result if it is a wait-before-signal
    std::optional<SignalInfo> OnTimelineWait(VkSemaphore semaphore, uint64_t wait_value);

    SignalsUpdate(const SyncValidator &sync_validator) : sync_validator_(sync_validator) {}

  private:
    void OnBinarySignal(const vvl::Semaphore &semaphore_state, const std::shared_ptr<QueueBatchContext> &batch,
                        const VkSemaphoreSubmitInfo &submit_signal);
    // Return false if signal is invalid (non-increasing value)
    bool OnTimelineSignal(const vvl::Semaphore &semaphore_state, const std::shared_ptr<QueueBatchContext> &batch,
                          const VkSemaphoreSubmitInfo &submit_signal);

  private:
    const SyncValidator &sync_validator_;
};

struct FenceHostSyncPoint {
    QueueId queue_id = kQueueIdInvalid;
    ResourceUsageTag tag = 0;
    AcquiredImage acquired;  // Iff queue == invalid and acquired.image valid.
};

struct TimelineHostSyncPoint {
    QueueId queue_id = 0;
    ResourceUsageTag tag = 0;
    uint64_t timeline_value = 0;
};

struct PresentedImageRecord {
    ResourceUsageTag tag;  // the global tag at presentation
    uint32_t image_index;
    uint32_t present_index;
    std::weak_ptr<vvl::Swapchain> swapchain_state;
    std::shared_ptr<const vvl::Image> image;
};

struct PresentedImage : public PresentedImageRecord {
    std::shared_ptr<QueueBatchContext> batch;
    subresource_adapter::ImageRangeGenerator range_gen;

    PresentedImage() = default;
    void UpdateMemoryAccess(SyncAccessIndex usage, ResourceUsageTag tag, AccessContext &access_context) const;
    PresentedImage(SyncValidator &sync_state, std::shared_ptr<QueueBatchContext> batch, VkSwapchainKHR swapchain,
                   uint32_t image_index, uint32_t present_index, ResourceUsageTag present_tag_);
    // For non-previsously presented images..
    PresentedImage(std::shared_ptr<vvl::Swapchain> &&swapchain, uint32_t at_index);
    bool Invalid() const;
    void ExportToSwapchain(SyncValidator &);
    void SetImage(uint32_t at_index);
};
using PresentedImages = std::vector<PresentedImage>;

// Store references to ResourceUsageRecords with global tag range within a batch
class BatchAccessLog {
  public:
    struct BatchRecord {
        const QueueSyncState *queue = nullptr;
        uint64_t submit_index = 0;
        uint32_t batch_index = 0;
        uint32_t cb_index = 0;
        ResourceUsageTag base_tag = 0;
    };

    struct AccessRecord {
        const BatchRecord *batch;
        const ResourceUsageRecord *record;
        const DebugNameProvider *debug_name_provider;
        bool IsValid() const { return batch && record; }
    };

    struct CBSubmitLog : DebugNameProvider {
      public:
        CBSubmitLog() = default;
        CBSubmitLog(const CBSubmitLog &batch) = default;
        CBSubmitLog(CBSubmitLog &&other) = default;
        CBSubmitLog &operator=(const CBSubmitLog &other) = default;
        CBSubmitLog &operator=(CBSubmitLog &&other) = default;
        CBSubmitLog(const BatchRecord &batch, std::shared_ptr<const CommandExecutionContext::CommandBufferSet> cbs,
                    std::shared_ptr<const CommandExecutionContext::AccessLog> log);
        CBSubmitLog(const BatchRecord &batch, const CommandBufferAccessContext &cb,
                    const std::vector<std::string> &initial_label_stack);
        size_t Size() const { return log_->size(); }
        AccessRecord GetAccessRecord(ResourceUsageTag tag) const;

        // DebugNameProvider
        std::string GetDebugRegionName(const ResourceUsageRecord &record) const override;

      private:
        BatchRecord batch_;
        std::shared_ptr<const CommandExecutionContext::CommandBufferSet> cbs_;
        std::shared_ptr<const CommandExecutionContext::AccessLog> log_;
        // label stack at the point when command buffer is submitted to the queue
        std::vector<std::string> initial_label_stack_;
    };

    void Import(const BatchRecord &batch, const CommandBufferAccessContext &cb_access,
                const std::vector<std::string> &initial_label_stack);
    void Import(const BatchAccessLog &other);
    void Insert(const BatchRecord &batch, const ResourceUsageRange &range,
                std::shared_ptr<const CommandExecutionContext::AccessLog> log);

    void Trim(const ResourceUsageTagSet &used);
    // AccessRecord lookup is based on global tags
    AccessRecord GetAccessRecord(ResourceUsageTag tag) const;
    BatchAccessLog() {}

  private:
    using CBSubmitLogRangeMap = sparse_container::range_map<ResourceUsageTag, CBSubmitLog>;
    CBSubmitLogRangeMap log_map_;
};

// Batch that has wait-before-signal dependencies.
struct UnresolvedBatch {
    BatchContextPtr batch;
    uint64_t submit_index = 0;
    uint32_t batch_index = 0;
    std::vector<CommandBufferConstPtr> command_buffers;

    // Waits-before-signals that prevent this batch from being resolved.
    // When the wait is resolved it is removed from this list and the batch
    // from the resolving signal is stored in resolved_dependencies array.
    std::vector<VkSemaphoreSubmitInfo> unresolved_waits;

    // The batches from the resolved dependencies. They are used for async validaton.
    // This includes the batches from the resolved waits and also the last batch
    // (prior batch on the same queue).
    std::vector<BatchContextConstPtr> resolved_dependencies;

    // Signals to signal when all the waits are resolved.
    std::vector<VkSemaphoreSubmitInfo> signals;

    // Queue's label stack at the beginning of this batch
    std::vector<std::string> label_stack;
};

// Helper struct to resolve wait-before-signal
struct UnresolvedQueue {
    std::shared_ptr<QueueSyncState> queue_state;
    std::vector<UnresolvedBatch> unresolved_batches;
    // whether unresolved state should be updated for this queue
    bool update_unresolved = false;
};

class QueueBatchContext : public CommandExecutionContext, public std::enable_shared_from_this<QueueBatchContext> {
  public:
    class PresentResourceRecord : public AlternateResourceUsage::RecordBase {
      public:
        using Base_ = AlternateResourceUsage::RecordBase;
        Base_::Record MakeRecord() const override;
        ~PresentResourceRecord() override {}
        PresentResourceRecord(const PresentedImageRecord &presented) : presented_(presented) {}
        vvl::Func GetCommand() const override { return vvl::Func::vkQueuePresentKHR; }

      private:
        PresentedImageRecord presented_;
    };

    class AcquireResourceRecord : public AlternateResourceUsage::RecordBase {
      public:
        using Base_ = AlternateResourceUsage::RecordBase;
        Base_::Record MakeRecord() const override;
        AcquireResourceRecord(const PresentedImageRecord &presented, ResourceUsageTag tag, vvl::Func command)
            : presented_(presented), acquire_tag_(tag), command_(command) {}
        vvl::Func GetCommand() const override { return command_; }

      private:
        PresentedImageRecord presented_;
        ResourceUsageTag acquire_tag_;
        vvl::Func command_;
    };

    using Ptr = std::shared_ptr<QueueBatchContext>;
    using ConstPtr = std::shared_ptr<const QueueBatchContext>;

    QueueBatchContext(const SyncValidator &sync_state, const QueueSyncState &queue_state);
    QueueBatchContext(const SyncValidator &sync_state);
    QueueBatchContext() = delete;
    ~QueueBatchContext();
    void Trim();

    ResourceUsageInfo GetResourceUsageInfo(ResourceUsageTagEx tag_ex) const override;
    AccessContext *GetCurrentAccessContext() override { return current_access_context_; }
    const AccessContext *GetCurrentAccessContext() const override { return current_access_context_; }
    SyncEventsContext *GetCurrentEventsContext() override { return &events_context_; }
    const SyncEventsContext *GetCurrentEventsContext() const override { return &events_context_; }
    const QueueSyncState *GetQueueSyncState() { return queue_state_; }
    QueueId GetQueueId() const override;
    ResourceUsageRange GetTagRange() const { return tag_range_; }

    ResourceUsageTag SetupBatchTags(uint32_t tag_count);
    void ResetEventsContext() { events_context_.Clear(); }

    // For Submit
    std::vector<BatchContextConstPtr> ResolveSubmitWaits(vvl::span<const VkSemaphoreSubmitInfo> wait_semaphores,
                                                         std::vector<VkSemaphoreSubmitInfo> &unresolved_waits,
                                                         SignalsUpdate &signals_update);

    bool ValidateSubmit(const std::vector<CommandBufferConstPtr> &command_buffers, uint64_t submit_index, uint32_t batch_index,
                        std::vector<std::string> &current_label_stack, const ErrorObject &error_obj);
    void ResolveSubmittedCommandBuffer(const AccessContext &recorded_context, ResourceUsageTag offset);

    // For Present
    std::vector<ConstPtr> ResolvePresentWaits(vvl::span<const VkSemaphore> wait_semaphores, const PresentedImages &presented_images,
                                              SignalsUpdate &signals_update);
    bool DoQueuePresentValidate(const Location &loc, const PresentedImages &presented_images);
    void DoPresentOperations(const PresentedImages &presented_images);
    void LogPresentOperations(const PresentedImages &presented_images, uint64_t submit_index);

    // For Acquire
    void SetupAccessContext(const PresentedImage &presented);
    void DoAcquireOperation(const PresentedImage &presented);
    void LogAcquireOperation(const PresentedImage &presented, vvl::Func command);

    VulkanTypedHandle Handle() const override;

    template <typename Predicate>
    void ApplyPredicatedWait(Predicate &predicate);
    void ApplyTaggedWait(QueueId queue_id, ResourceUsageTag tag);
    void ApplyAcquireWait(const AcquiredImage &acquired);
    void OnResourceDestroyed(const ResourceAccessRange &resource_range);

    void BeginRenderPassReplaySetup(ReplayState &replay, const SyncOpBeginRenderPass &begin_op);
    void NextSubpassReplaySetup(ReplayState &replay);
    void EndRenderPassReplayCleanup(ReplayState &replay);

    [[nodiscard]] std::vector<ConstPtr> RegisterAsyncContexts(const std::vector<ConstPtr> &batches_resolved);
    void ResolveLastBatch(const QueueBatchContext::ConstPtr &last_batch);

    void ResolveSubmitSemaphoreWait(const SignalInfo &signal_info, VkPipelineStageFlags2 wait_mask);
    void ImportTags(const QueueBatchContext &from);

  private:
    void ResolvePresentSemaphoreWait(const SignalInfo &signal_info, const PresentedImages &presented_images);

  private:
    const QueueSyncState *queue_state_ = nullptr;
    ResourceUsageRange tag_range_ = ResourceUsageRange(0, 0);  // Range of tags referenced by cbs_referenced

    AccessContext access_context_;
    AccessContext *current_access_context_;
    SyncEventsContext events_context_;
    BatchAccessLog batch_log_;
    std::vector<ResourceUsageTag> queue_sync_tag_;
};

class QueueSyncState {
  public:
    QueueSyncState(const std::shared_ptr<vvl::Queue> &queue_state, QueueId id) : id_(id), queue_state_(queue_state) {}

    VulkanTypedHandle Handle() const { return queue_state_->Handle(); }
    const vvl::Queue *GetQueueState() const { return queue_state_.get(); }
    VkQueueFlags GetQueueFlags() const { return queue_state_->queue_family_properties.queueFlags; }
    QueueId GetQueueId() const { return id_; }
    // Method is const but updates mutable sumbit_index atomically.
    uint64_t ReserveSubmitId() const;

    // Last batch state management.
    // The Validate phase makes a request to update last batch by calling SetPendingLastBatch.
    // Then the Record phase actually updates the last batch by calling ApplyPendingLastBatch.
    // Pending last batch is a mutable state. It relies on the queue external synchronization.
    QueueBatchContext::ConstPtr LastBatch() const { return last_batch_; }
    QueueBatchContext::Ptr LastBatch() { return last_batch_; }
    void SetPendingLastBatch(QueueBatchContext::Ptr &&last) const;
    void ApplyPendingLastBatch();
    QueueBatchContext::Ptr PendingLastBatch() const { return pending_last_batch_; }

    // Unresolved batches state management.
    // The Validate phase makes request to update the list of unresolved batches by calling SetPendingUnresolvedBatches.
    // Then the Record phase actually updates the list of unresolved batches by calling ApplyPendingLastBatch.
    // Pending unresovled batches is a mutable state. It relies on the queue external synchronization.
    const std::vector<UnresolvedBatch> &UnresolvedBatches() const { return unresolved_batches_; }
    void SetPendingUnresolvedBatches(std::vector<UnresolvedBatch> &&unresolved_batches) const;
    void ApplyPendingUnresolvedBatches();
    const std::vector<UnresolvedBatch> &PendingUnresolvedBatches() const { return pending_unresolved_batches_; }

    // Called by the Validate methods to ensure no pending state is left.
    // Pending state is automatically cleared in PostRecord calls,
    // the only exception is when validation error happens.
    void ClearPending() const;

  private:
    const QueueId id_;
    std::shared_ptr<vvl::Queue> queue_state_;
    mutable std::atomic<uint64_t> submit_index_ = 0;

    QueueBatchContext::Ptr last_batch_;

    // The first batch in the unresolved batches list is always due to the wait-before-signal dependency.
    // All subsequent batches from the same queue must also be stored here because they can't be processed
    // until the wait-before-signal dependency is resolved (respect submission order). When the first batch
    // is resolved, we start processing other queued batches until we uncoutner a batch with unresolved
    // wait-before-signal (it becomes the new head of the list) or the list is empty.
    std::vector<UnresolvedBatch> unresolved_batches_;

    mutable QueueBatchContext::Ptr pending_last_batch_;
    mutable std::vector<UnresolvedBatch> pending_unresolved_batches_;
    mutable bool update_unresolved_batches_ = false;
};

struct QueueSubmitCmdState {
    std::shared_ptr<const QueueSyncState> queue;
    SignalsUpdate signals_update;
    QueueSubmitCmdState(const SyncValidator &sync_validator) : signals_update(sync_validator) {}
};