blob: 3d9fd62db4001586e140c311672f32b97933a180 [file] [log] [blame]
/*
* Copyright (c) 2019-2023 Valve Corporation
* Copyright (c) 2019-2023 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 <limits>
#include <memory>
#include <set>
#include <vulkan/vulkan.h>
#include "generated/sync_validation_types.h"
#include "state_tracker/state_tracker.h"
#include "state_tracker/cmd_buffer_state.h"
#include "state_tracker/render_pass_state.h"
class AccessContext;
class CommandBufferAccessContext;
class CommandExecutionContext;
struct PresentedImage;
class QueueBatchContext;
struct QueueSubmitCmdState;
class RenderPassAccessContext;
class ReplayState;
class ResourceAccessState;
class ResourceAccessWriteState;
struct ResourceFirstAccess;
class SyncEventsContext;
struct SyncEventState;
class SyncValidator;
using ImageRangeGen = subresource_adapter::ImageRangeGenerator;
namespace syncval_state {
class CommandBuffer;
class Swapchain;
class ImageState : public IMAGE_STATE {
public:
ImageState(const ValidationStateTracker *dev_data, VkImage img, const VkImageCreateInfo *pCreateInfo,
VkFormatFeatureFlags2KHR features)
: IMAGE_STATE(dev_data, img, pCreateInfo, features), opaque_base_address_(0U) {}
ImageState(const ValidationStateTracker *dev_data, VkImage img, const VkImageCreateInfo *pCreateInfo, VkSwapchainKHR swapchain,
uint32_t swapchain_index, VkFormatFeatureFlags2KHR features)
: IMAGE_STATE(dev_data, img, pCreateInfo, swapchain, swapchain_index, features), opaque_base_address_(0U) {}
bool IsLinear() const { return fragment_encoder->IsLinearImage(); }
bool IsTiled() const { return !IsLinear(); }
bool IsSimplyBound() const;
void SetOpaqueBaseAddress(ValidationStateTracker &dev_data);
VkDeviceSize GetOpaqueBaseAddress() const { return opaque_base_address_; }
bool HasOpaqueMapping() const { return 0U != opaque_base_address_; }
VkDeviceSize GetResourceBaseAddress() const;
ImageRangeGen MakeImageRangeGen(const VkImageSubresourceRange &subresource_range, bool is_depth_sliced) const;
ImageRangeGen MakeImageRangeGen(const VkImageSubresourceRange &subresource_range, const VkOffset3D &offset,
const VkExtent3D &extent, bool is_depth_sliced) const;
protected:
VkDeviceSize opaque_base_address_ = 0U;
};
class ImageViewState : public IMAGE_VIEW_STATE {
public:
ImageViewState(const std::shared_ptr<IMAGE_STATE> &image_state, VkImageView iv, const VkImageViewCreateInfo *ci,
VkFormatFeatureFlags2KHR ff, const VkFilterCubicImageViewImageFormatPropertiesEXT &cubic_props);
const ImageState *GetImageState() const { return static_cast<const syncval_state::ImageState *>(image_state.get()); }
ImageRangeGen MakeImageRangeGen(const VkOffset3D &offset, const VkExtent3D &extent, VkImageAspectFlags aspect_mask = 0) const;
const ImageRangeGen &GetFullViewImageRangeGen() const { return view_range_gen; }
protected:
ImageRangeGen MakeImageRangeGen() const;
// All data members needs for MakeImageRangeGen() must be set before initializing view_range_gen... i.e. above this line.
const ImageRangeGen view_range_gen;
};
} // namespace syncval_state
VALSTATETRACK_DERIVED_STATE_OBJECT(VkImage, syncval_state::ImageState, IMAGE_STATE)
VALSTATETRACK_DERIVED_STATE_OBJECT(VkImageView, syncval_state::ImageViewState, IMAGE_VIEW_STATE)
using QueueId = uint32_t;
enum SyncHazard {
NONE = 0,
READ_AFTER_WRITE,
WRITE_AFTER_READ,
WRITE_AFTER_WRITE,
READ_RACING_WRITE,
WRITE_RACING_WRITE,
WRITE_RACING_READ,
WRITE_AFTER_PRESENT, // Once presented, an image may not be used until acquired
READ_AFTER_PRESENT,
PRESENT_AFTER_READ, // Must be unreferenced and visible to present
PRESENT_AFTER_WRITE,
};
enum class SyncOrdering : uint8_t {
kNonAttachment = 0,
kColorAttachment = 1,
kDepthStencilAttachment = 2,
kRaster = 3,
kNumOrderings = 4,
};
// Useful Utilites for manipulating StageAccess parameters, suitable as base class to save typing
struct SyncStageAccess {
static inline const SyncStageAccessInfoType &UsageInfo(SyncStageAccessIndex stage_access_index) {
return syncStageAccessInfoByStageAccessIndex()[stage_access_index];
}
static inline SyncStageAccessFlags FlagBit(SyncStageAccessIndex stage_access) {
return syncStageAccessInfoByStageAccessIndex()[stage_access].stage_access_bit;
}
static bool IsRead(SyncStageAccessIndex stage_access_index) { return syncStageAccessReadMask[stage_access_index]; }
static bool IsRead(const SyncStageAccessInfoType &info) { return IsRead(info.stage_access_index); }
static bool IsWrite(SyncStageAccessIndex stage_access_index) { return syncStageAccessWriteMask[stage_access_index]; }
static bool IsWrite(const SyncStageAccessInfoType &info) { return IsWrite(info.stage_access_index); }
static VkPipelineStageFlags2KHR PipelineStageBit(SyncStageAccessIndex stage_access_index) {
return syncStageAccessInfoByStageAccessIndex()[stage_access_index].stage_mask;
}
static SyncStageAccessFlags AccessScopeByStage(VkPipelineStageFlags2KHR stages);
static SyncStageAccessFlags AccessScopeByAccess(VkAccessFlags2KHR access);
static SyncStageAccessFlags AccessScope(VkPipelineStageFlags2KHR stages, VkAccessFlags2KHR access);
static SyncStageAccessFlags AccessScope(const SyncStageAccessFlags &stage_scope, VkAccessFlags2KHR accesses) {
return stage_scope & AccessScopeByAccess(accesses);
}
};
class AlternateResourceUsage {
public:
struct RecordBase;
struct RecordBase {
using Record = std::unique_ptr<RecordBase>;
virtual Record MakeRecord() const = 0;
virtual std::ostream &Format(std::ostream &out, const SyncValidator &sync_state) const = 0;
virtual ~RecordBase() {}
};
struct FormatterState {
FormatterState(const SyncValidator &sync_state_, const AlternateResourceUsage &usage_)
: sync_state(sync_state_), usage(usage_) {}
const SyncValidator &sync_state;
const AlternateResourceUsage &usage;
};
FormatterState Formatter(const SyncValidator &sync_state) const { return FormatterState(sync_state, *this); };
std::ostream &Format(std::ostream &out, const SyncValidator &sync_state) const { return record_->Format(out, sync_state); };
AlternateResourceUsage() = default;
AlternateResourceUsage(const RecordBase &record) : record_(record.MakeRecord()) {}
AlternateResourceUsage(const AlternateResourceUsage &other) : record_() {
if (bool(other.record_)) {
record_ = other.record_->MakeRecord();
}
}
AlternateResourceUsage &operator=(const AlternateResourceUsage &other) {
if (bool(other.record_)) {
record_ = other.record_->MakeRecord();
} else {
record_.reset();
}
return *this;
}
operator bool() const { return bool(record_); }
private:
RecordBase::Record record_;
};
inline std::ostream &operator<<(std::ostream &out, const AlternateResourceUsage::FormatterState &formatter) {
formatter.usage.Format(out, formatter.sync_state);
return out;
}
template <typename State, typename T>
struct FormatterImpl {
using That = T;
friend T;
const State &state;
const That &that;
private:
// Only intended to be invoke with from That method
FormatterImpl(const State &state_, const That &that_) : state(state_), that(that_) {}
};
struct NamedHandle {
const static size_t kInvalidIndex = std::numeric_limits<size_t>::max();
std::string name;
VulkanTypedHandle handle;
size_t index = kInvalidIndex;
using FormatterState = FormatterImpl<SyncValidator, NamedHandle>;
// NOTE: CRTP could DRY this
FormatterState Formatter(const SyncValidator &sync_state) const { return FormatterState(sync_state, *this); }
NamedHandle() = default;
NamedHandle(const NamedHandle &other) = default;
NamedHandle(NamedHandle &&other) = default;
NamedHandle(const std::string &name_, const VulkanTypedHandle &handle_, size_t index_ = kInvalidIndex)
: name(name_), handle(handle_), index(index_) {}
NamedHandle(const char *name_, const VulkanTypedHandle &handle_, size_t index_ = kInvalidIndex)
: name(name_), handle(handle_), index(index_) {}
NamedHandle(const VulkanTypedHandle &handle_) : name(), handle(handle_) {}
NamedHandle &operator=(const NamedHandle &other) = default;
NamedHandle &operator=(NamedHandle &&other) = default;
operator bool() const { return (handle.handle != 0U) && (handle.type != VulkanObjectType::kVulkanObjectTypeUnknown); }
bool IsIndexed() const { return index != kInvalidIndex; }
};
struct ResourceCmdUsageRecord {
using TagIndex = size_t;
using Count = uint32_t;
constexpr static TagIndex kMaxIndex = std::numeric_limits<TagIndex>::max();
enum class SubcommandType { kNone, kSubpassTransition, kLoadOp, kStoreOp, kResolveOp, kIndex };
ResourceCmdUsageRecord() = default;
ResourceCmdUsageRecord(vvl::Func command_, Count seq_num_, SubcommandType sub_type_, Count sub_command_,
const CMD_BUFFER_STATE *cb_state_, Count reset_count_)
: command(command_),
seq_num(seq_num_),
sub_command_type(sub_type_),
sub_command(sub_command_),
cb_state(cb_state_),
reset_count(reset_count_) {}
// NamedHandle must be constructable from args
template <class... Args>
void AddHandle(Args &&...args) {
handles.emplace_back(std::forward<Args>(args)...);
}
vvl::Func command = vvl::Func::Empty;
Count seq_num = 0U;
SubcommandType sub_command_type = SubcommandType::kNone;
Count sub_command = 0U;
// This is somewhat repetitive, but it prevents the need for Exec/Submit time touchup, after which usage records can be
// from different command buffers and resets.
// plain pointer as a shared pointer is held by the context storing this record
const CMD_BUFFER_STATE *cb_state = nullptr;
Count reset_count;
small_vector<NamedHandle, 1> handles;
};
struct ResourceUsageRecord : public ResourceCmdUsageRecord {
struct FormatterState {
FormatterState(const SyncValidator &sync_state_, const ResourceUsageRecord &record_, const CMD_BUFFER_STATE *cb_state_)
: sync_state(sync_state_), record(record_), ex_cb_state(cb_state_) {}
const SyncValidator &sync_state;
const ResourceUsageRecord &record;
const CMD_BUFFER_STATE *ex_cb_state;
};
FormatterState Formatter(const SyncValidator &sync_state, const CMD_BUFFER_STATE *ex_cb_state) const {
return FormatterState(sync_state, *this, ex_cb_state);
}
AlternateResourceUsage alt_usage;
ResourceUsageRecord() = default;
ResourceUsageRecord(vvl::Func command_, Count seq_num_, SubcommandType sub_type_, Count sub_command_,
const CMD_BUFFER_STATE *cb_state_, Count reset_count_)
: ResourceCmdUsageRecord(command_, seq_num_, sub_type_, sub_command_, cb_state_, reset_count_) {}
ResourceUsageRecord(const AlternateResourceUsage &other) : ResourceCmdUsageRecord(), alt_usage(other) {}
ResourceUsageRecord(const ResourceUsageRecord &other) : ResourceCmdUsageRecord(other), alt_usage(other.alt_usage) {}
ResourceUsageRecord &operator=(const ResourceUsageRecord &other) = default;
};
// The resource tag index is relative to the command buffer or queue in which it's found
using ResourceUsageTag = ResourceUsageRecord::TagIndex;
// Notes:
// * Key must be integral.
// * We aren't interested as of this implementation in caching lookups, only inserts
// * using a raw C-style array instead of std::array intentionally for size/performance reasons
//
// The following were shown to not improve hit rate for current usage (tag set gathering). For general use YMMV.
// * More complicated index construction (at >> LogSize ^ at)
// * Multi-way LRU eviction caching (equivalent hit rate to 1-way direct replacement of same total cache slots) but with
// higher complexity.
template <typename IntegralKey, size_t LogSize = 4U, IntegralKey kInvalidKey = IntegralKey(0)>
class CachedInsertSet : public std::set<IntegralKey> {
public:
using Base = std::set<IntegralKey>;
using key_type = typename Base::key_type;
using Index = unsigned;
static constexpr Index kSize = 1 << LogSize;
static constexpr key_type kMask = static_cast<key_type>(kSize) - 1;
void CachedInsert(const key_type key) {
// 1-way direct replacement
const Index index = static_cast<Index>(key & kMask); // Simplest
if (entries_[index] != key) {
entries_[index] = key;
Base::insert(key);
}
}
CachedInsertSet() { std::fill(entries_, entries_ + kSize, kInvalidKey); }
private:
key_type entries_[kSize];
};
using ResourceUsageTagSet = CachedInsertSet<ResourceUsageTag, 4>;
using ResourceUsageRange = sparse_container::range<ResourceUsageTag>;
class HazardResult {
public:
struct HazardState {
std::unique_ptr<const ResourceAccessState> access_state;
std::unique_ptr<const ResourceFirstAccess> recorded_access;
SyncStageAccessIndex usage_index = std::numeric_limits<SyncStageAccessIndex>::max();
SyncStageAccessFlags prior_access;
ResourceUsageTag tag = ResourceUsageTag();
SyncHazard hazard = NONE;
HazardState(const ResourceAccessState *access_state_, const SyncStageAccessInfoType &usage_info_, SyncHazard hazard_,
const SyncStageAccessFlags &prior_, ResourceUsageTag tag_);
};
void Set(const ResourceAccessState *access_state_, const SyncStageAccessInfoType &usage_info_, SyncHazard hazard_,
const ResourceAccessWriteState &prior_write);
void Set(const ResourceAccessState *access_state_, const SyncStageAccessInfoType &usage_info_, SyncHazard hazard_,
const SyncStageAccessFlags &prior_, ResourceUsageTag tag_);
void AddRecordedAccess(const ResourceFirstAccess &first_access);
bool IsHazard() const { return state_.has_value() && NONE != state_->hazard; }
bool IsWAWHazard() const;
ResourceUsageTag Tag() const {
assert(state_);
return state_->tag;
}
SyncHazard Hazard() const {
assert(state_);
return state_->hazard;
}
const std::unique_ptr<const ResourceFirstAccess> &RecordedAccess() const {
assert(state_);
return state_->recorded_access;
}
const HazardState &State() const {
assert(state_);
return state_.value();
}
private:
std::optional<HazardState> state_;
};
struct SyncExecScope {
VkPipelineStageFlags2KHR mask_param; // the xxxStageMask parameter passed by the caller
VkPipelineStageFlags2KHR
expanded_mask; // all stage bits covered by any 'catch all bits' in the parameter (eg. ALL_GRAPHICS_BIT).
VkPipelineStageFlags2KHR exec_scope; // all earlier or later stages that would be affected by a barrier using this scope.
SyncStageAccessFlags valid_accesses; // all valid accesses that can be used with this scope.
SyncExecScope() : mask_param(0), expanded_mask(0), exec_scope(0), valid_accesses(0) {}
SyncExecScope(VkPipelineStageFlags2KHR mask_param_, VkPipelineStageFlags2KHR expanded_mask_,
VkPipelineStageFlags2KHR exec_scope_, const SyncStageAccessFlags &valid_accesses_)
: mask_param(mask_param_), expanded_mask(expanded_mask_), exec_scope(exec_scope_), valid_accesses(valid_accesses_) {}
static SyncExecScope MakeSrc(VkQueueFlags queue_flags, VkPipelineStageFlags2KHR src_stage_mask,
const VkPipelineStageFlags2KHR disabled_feature_mask = 0);
static SyncExecScope MakeDst(VkQueueFlags queue_flags, VkPipelineStageFlags2KHR src_stage_mask);
};
struct SyncBarrier {
struct AllAccess {};
SyncExecScope src_exec_scope;
SyncStageAccessFlags src_access_scope;
SyncExecScope dst_exec_scope;
SyncStageAccessFlags dst_access_scope;
SyncBarrier() = default;
SyncBarrier(const SyncBarrier &other) = default;
SyncBarrier &operator=(const SyncBarrier &) = default;
SyncBarrier(const SyncExecScope &src, const SyncExecScope &dst);
SyncBarrier(const SyncExecScope &src, const SyncExecScope &dst, const AllAccess &);
SyncBarrier(const SyncExecScope &src_exec, const SyncStageAccessFlags &src_access, const SyncExecScope &dst_exec,
const SyncStageAccessFlags &dst_access)
: src_exec_scope(src_exec), src_access_scope(src_access), dst_exec_scope(dst_exec), dst_access_scope(dst_access) {}
template <typename Barrier>
SyncBarrier(const Barrier &barrier, const SyncExecScope &src, const SyncExecScope &dst);
SyncBarrier(VkQueueFlags queue_flags, const VkSubpassDependency2 &barrier);
// template constructor for sync2 barriers
template <typename Barrier>
SyncBarrier(VkQueueFlags queue_flags, const Barrier &barrier);
void Merge(const SyncBarrier &other) {
// Note that after merge, only the exec_scope and access_scope fields are fully valid
// TODO: Do we need to update any of the other fields? Merging has limited application.
src_exec_scope.exec_scope |= other.src_exec_scope.exec_scope;
src_access_scope |= other.src_access_scope;
dst_exec_scope.exec_scope |= other.dst_exec_scope.exec_scope;
dst_access_scope |= other.dst_access_scope;
}
};
struct SemaphoreScope : SyncExecScope {
SemaphoreScope(QueueId qid, const SyncExecScope &exec_scope) : SyncExecScope(exec_scope), queue(qid) {}
SemaphoreScope() = default;
QueueId queue;
};
struct AcquiredImage {
std::shared_ptr<const syncval_state::ImageState> image;
subresource_adapter::ImageRangeGenerator generator;
ResourceUsageTag present_tag;
ResourceUsageTag acquire_tag;
bool Invalid() const { return BASE_NODE::Invalid(image); }
AcquiredImage() = default;
AcquiredImage(const PresentedImage &presented, ResourceUsageTag acq_tag);
};
class SignaledSemaphores {
public:
// Is the record of a signaled semaphore, deleted when unsignaled
struct Signal {
Signal() = delete;
Signal(const Signal &other) = default;
Signal(Signal &&other) = default;
Signal &operator=(const Signal &other) = default;
Signal &operator=(Signal &&other) = default;
Signal(const std::shared_ptr<const SEMAPHORE_STATE> &sem_state_, const std::shared_ptr<QueueBatchContext> &batch_,
const SyncExecScope &exec_scope_);
Signal(const std::shared_ptr<const SEMAPHORE_STATE> &sem_state_, const PresentedImage &presented, ResourceUsageTag acq_tag);
std::shared_ptr<const SEMAPHORE_STATE> sem_state;
std::shared_ptr<QueueBatchContext> batch;
// Use the SyncExecScope::valid_accesses for first access scope
SemaphoreScope first_scope;
// Swapchain specific support signal info
// IFF swapchain_image is non-null
// batch is the batch of the last present for the acquired image
// The address_type, range_generator pair further limit 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)
AcquiredImage acquired;
// TODO add timeline semaphore support.
};
using SignalMap = vvl::unordered_map<VkSemaphore, std::shared_ptr<Signal>>;
using iterator = SignalMap::iterator;
using const_iterator = SignalMap::const_iterator;
using mapped_type = SignalMap::mapped_type;
iterator begin() { return signaled_.begin(); }
const_iterator begin() const { return signaled_.begin(); }
iterator end() { return signaled_.end(); }
const_iterator end() const { return signaled_.end(); }
bool SignalSemaphore(const std::shared_ptr<const SEMAPHORE_STATE> &sem_state, const std::shared_ptr<QueueBatchContext> &batch,
const VkSemaphoreSubmitInfo &signal_info);
bool Insert(const std::shared_ptr<const SEMAPHORE_STATE> &sem_state, std::shared_ptr<Signal> &&signal);
bool SignalSemaphore(const std::shared_ptr<const SEMAPHORE_STATE> &sem_state, const PresentedImage &presented,
ResourceUsageTag acq_tag);
std::shared_ptr<const Signal> Unsignal(VkSemaphore);
void Resolve(SignaledSemaphores &parent, std::shared_ptr<QueueBatchContext> &last_batch);
SignaledSemaphores() : prev_(nullptr) {}
SignaledSemaphores(const SignaledSemaphores &prev) : prev_(&prev) {}
private:
void Import(VkSemaphore sem, std::shared_ptr<Signal> &&move_from);
void Reset();
std::shared_ptr<const Signal> GetPrev(VkSemaphore sem) const;
vvl::unordered_map<VkSemaphore, std::shared_ptr<Signal>> signaled_;
const SignaledSemaphores *prev_; // Allowing this type to act as a writable overlay
};
struct ResourceFirstAccess {
ResourceUsageTag tag;
const SyncStageAccessInfoType *usage_info;
SyncOrdering ordering_rule;
ResourceFirstAccess(ResourceUsageTag tag_, const SyncStageAccessInfoType &usage_info_, SyncOrdering ordering_rule_)
: tag(tag_), usage_info(&usage_info_), ordering_rule(ordering_rule_){};
ResourceFirstAccess(const ResourceFirstAccess &other) = default;
ResourceFirstAccess(ResourceFirstAccess &&other) = default;
ResourceFirstAccess &operator=(const ResourceFirstAccess &rhs) = default;
ResourceFirstAccess &operator=(ResourceFirstAccess &&rhs) = default;
bool operator==(const ResourceFirstAccess &rhs) const {
return (tag == rhs.tag) && (usage_info == rhs.usage_info) && (ordering_rule == rhs.ordering_rule);
}
};
using QueueId = uint32_t;
struct OrderingBarrier {
VkPipelineStageFlags2KHR exec_scope;
SyncStageAccessFlags access_scope;
OrderingBarrier() = default;
OrderingBarrier(const OrderingBarrier &) = default;
OrderingBarrier(VkPipelineStageFlags2KHR es, SyncStageAccessFlags as) : exec_scope(es), access_scope(as) {}
OrderingBarrier &operator=(const OrderingBarrier &) = default;
OrderingBarrier &operator|=(const OrderingBarrier &rhs) {
exec_scope |= rhs.exec_scope;
access_scope |= rhs.access_scope;
return *this;
}
bool operator==(const OrderingBarrier &rhs) const {
return (exec_scope == rhs.exec_scope) && (access_scope == rhs.access_scope);
}
};
class ResourceAccessWriteState {
public:
bool operator==(const ResourceAccessWriteState &rhs) const {
return (access_ == rhs.access_) && (barriers_ == rhs.barriers_) && (tag_ == rhs.tag_) && (queue_ == rhs.queue_) &&
(dependency_chain_ == rhs.dependency_chain_);
}
bool WriteInChain(VkPipelineStageFlags2KHR src_exec_scope) const;
bool WriteInScope(const SyncStageAccessFlags &src_access_scope) const;
bool WriteBarrierInScope(const SyncStageAccessFlags &src_access_scope) const;
bool WriteInSourceScopeOrChain(VkPipelineStageFlags2KHR src_exec_scope, SyncStageAccessFlags src_access_scope) const;
bool WriteInQueueSourceScopeOrChain(QueueId queue, VkPipelineStageFlags2KHR src_exec_scope,
const SyncStageAccessFlags &src_access_scope) const;
bool WriteInEventScope(VkPipelineStageFlags2KHR src_exec_scope, const SyncStageAccessFlags &src_access_scope,
QueueId scope_queue, ResourceUsageTag scope_tag) const;
bool WriteInChainedScope(VkPipelineStageFlags2KHR src_exec_scope, const SyncStageAccessFlags &src_access_scope) const;
ResourceAccessWriteState(const SyncStageAccessInfoType &usage_info, ResourceUsageTag tag);
ResourceAccessWriteState() = default;
SyncStageAccessIndex Index() const { return access_->stage_access_index; }
bool IsIndex(SyncStageAccessIndex usage_index) const { return Index() == usage_index; }
bool IsQueue(QueueId other_queue) const { return queue_ == other_queue; }
const SyncStageAccessInfoType &Access() const { return *access_; }
const SyncStageAccessFlags &Barriers() const { return barriers_; }
ResourceUsageTag Tag() const { return tag_; }
bool IsWriteHazard(const SyncStageAccessInfoType &usage_info) const;
bool IsOrdered(const OrderingBarrier &ordering, QueueId queue_id) const;
bool IsOrderedWriteHazard(VkPipelineStageFlags2KHR src_exec_scope, const SyncStageAccessFlags &src_access_scope) const;
bool IsWriteBarrierHazard(QueueId queue_id, VkPipelineStageFlags2KHR src_exec_scope,
const SyncStageAccessFlags &src_access_scope) const;
void SetQueueId(QueueId id);
void Set(const SyncStageAccessInfoType &usage_info, ResourceUsageTag tag);
void MergeBarriers(const ResourceAccessWriteState &other);
void OffsetTag(ResourceUsageTag offset) { tag_ += offset; }
bool HasPendingState() const { return pending_barriers_.any() || (0 != pending_dep_chain_); }
void ClearPending();
void UpdatePendingBarriers(const SyncBarrier &barrier);
void ApplyPendingBarriers();
void UpdatePendingLayoutOrdering(const SyncBarrier &barrier);
const OrderingBarrier &GetPendingLayoutOrdering() const { return pending_layout_ordering_; }
private:
const SyncStageAccessInfoType *access_;
SyncStageAccessFlags barriers_; // union of applicable barrier masks since last write
ResourceUsageTag tag_;
QueueId queue_;
// intially zero, but accumulating the dstStages of barriers if they chain.
VkPipelineStageFlags2KHR dependency_chain_;
// Write specific layout state
OrderingBarrier pending_layout_ordering_;
VkPipelineStageFlags2KHR pending_dep_chain_;
SyncStageAccessFlags pending_barriers_;
friend ResourceAccessState;
};
class ResourceAccessState : public SyncStageAccess {
protected:
using OrderingBarriers = std::array<OrderingBarrier, static_cast<size_t>(SyncOrdering::kNumOrderings)>;
using FirstAccesses = small_vector<ResourceFirstAccess, 3>;
public:
// Mutliple read operations can be simlutaneously (and independently) synchronized,
// given the only the second execution scope creates a dependency chain, we have to track each,
// but only up to one per pipeline stage (as another read from the *same* stage become more recent,
// and applicable one for hazard detection
struct ReadState {
VkPipelineStageFlags2KHR stage; // The stage of this read
SyncStageAccessFlags access; // TODO: Change to FlagBits when we have a None bit enum
// TODO: Revisit whether this needs to support multiple reads per stage
VkPipelineStageFlags2KHR barriers; // all applicable barriered stages
VkPipelineStageFlags2KHR sync_stages; // reads known to have happened after this
ResourceUsageTag tag;
QueueId queue;
VkPipelineStageFlags2KHR pending_dep_chain; // Should be zero except during barrier application
// Excluded from comparison
ReadState() = default;
ReadState(VkPipelineStageFlags2KHR stage_, SyncStageAccessFlags access_, VkPipelineStageFlags2KHR barriers_,
ResourceUsageTag tag_);
bool operator==(const ReadState &rhs) const {
return (stage == rhs.stage) && (access == rhs.access) && (barriers == rhs.barriers) &&
(sync_stages == rhs.sync_stages) && (tag == rhs.tag) && (queue == rhs.queue) &&
(pending_dep_chain == rhs.pending_dep_chain);
}
void Normalize() { pending_dep_chain = VK_PIPELINE_STAGE_2_NONE; }
bool IsReadBarrierHazard(VkPipelineStageFlags2KHR src_exec_scope) const {
// If the read stage is not in the src sync scope
// *AND* not execution chained with an existing sync barrier (that's the or)
// then the barrier access is unsafe (R/W after R)
return (src_exec_scope & (stage | barriers)) == 0;
}
bool IsReadBarrierHazard(QueueId barrier_queue, VkPipelineStageFlags2KHR src_exec_scope) const {
// If the read stage is not in the src sync scope
// *AND* not execution chained with an existing sync barrier (that's the or)
// then the barrier access is unsafe (R/W after R)
VkPipelineStageFlags2 queue_ordered_stage = (queue == barrier_queue) ? stage : VK_PIPELINE_STAGE_2_NONE;
return (src_exec_scope & (queue_ordered_stage | barriers)) == 0;
}
bool operator!=(const ReadState &rhs) const { return !(*this == rhs); }
void Set(VkPipelineStageFlags2KHR stage_, const SyncStageAccessFlags &access_, VkPipelineStageFlags2KHR barriers_,
ResourceUsageTag tag_);
bool ReadInScopeOrChain(VkPipelineStageFlags2 exec_scope) const { return (exec_scope & (stage | barriers)) != 0; }
bool ReadInQueueScopeOrChain(QueueId queue, VkPipelineStageFlags2 exec_scope) const;
bool ReadInEventScope(VkPipelineStageFlags2 exec_scope, QueueId scope_queue, ResourceUsageTag scope_tag) const {
// If this read is the same one we included in the set event and in scope, then apply the execution barrier...
// NOTE: That's not really correct... this read stage might *not* have been included in the setevent, and the barriers
// representing the chain might have changed since then (that would be an odd usage), so as a first approximation
// we'll assume the barriers *haven't* been changed since (if the tag hasn't), and while this could be a false
// positive in the case of Set; SomeBarrier; Wait; we'll live with it until we can add more state to the first scope
// capture (the specific write and read stages that *were* in scope at the moment of SetEvents.
return (tag < scope_tag) && ReadInQueueScopeOrChain(scope_queue, exec_scope);
}
void ApplyReadBarrier(VkPipelineStageFlags2KHR dst_scope) { pending_dep_chain |= dst_scope; }
VkPipelineStageFlags2 ApplyPendingBarriers();
};
HazardResult DetectHazard(const SyncStageAccessInfoType &usage_info) const;
HazardResult DetectHazard(const SyncStageAccessInfoType &usage_info, SyncOrdering ordering_rule, QueueId queue_id) const;
HazardResult DetectHazard(const SyncStageAccessInfoType &usage_info, const OrderingBarrier &ordering, QueueId queue_id) const;
HazardResult DetectHazard(const ResourceAccessState &recorded_use, QueueId queue_id, const ResourceUsageRange &tag_range) const;
HazardResult DetectAsyncHazard(const SyncStageAccessInfoType &usage_info, ResourceUsageTag start_tag) const;
HazardResult DetectAsyncHazard(const ResourceAccessState &recorded_use, const ResourceUsageRange &tag_range,
ResourceUsageTag start_tag) const;
HazardResult DetectBarrierHazard(const SyncStageAccessInfoType &usage_info, QueueId queue_id,
VkPipelineStageFlags2KHR source_exec_scope,
const SyncStageAccessFlags &source_access_scope) const;
HazardResult DetectBarrierHazard(const SyncStageAccessInfoType &usage_info, const ResourceAccessState &scope_state,
VkPipelineStageFlags2KHR source_exec_scope, const SyncStageAccessFlags &source_access_scope,
QueueId event_queue, ResourceUsageTag event_tag) const;
void Update(const SyncStageAccessInfoType &usage_info, SyncOrdering ordering_rule, ResourceUsageTag tag);
void SetWrite(const SyncStageAccessInfoType &usage_info, ResourceUsageTag tag);
void ClearWrite();
void ClearRead();
void ClearPending();
void ClearFirstUse();
void Resolve(const ResourceAccessState &other);
void ApplyBarriers(const std::vector<SyncBarrier> &barriers, bool layout_transition);
void ApplyBarriersImmediate(const std::vector<SyncBarrier> &barriers);
template <typename ScopeOps>
void ApplyBarrier(ScopeOps &&scope, const SyncBarrier &barrier, bool layout_transition);
void ApplyPendingBarriers(ResourceUsageTag tag);
void ApplySemaphore(const SemaphoreScope &signal, const SemaphoreScope wait);
struct WaitQueueTagPredicate {
QueueId queue;
ResourceUsageTag tag;
bool operator()(const ReadState &read_access) const; // Read access predicate
bool operator()(const ResourceAccessState &access) const; // Write access predicate
};
friend WaitQueueTagPredicate;
struct WaitTagPredicate {
ResourceUsageTag tag;
bool operator()(const ReadState &read_access) const; // Read access predicate
bool operator()(const ResourceAccessState &access) const; // Write access predicate
};
friend WaitTagPredicate;
struct WaitAcquirePredicate {
ResourceUsageTag present_tag;
ResourceUsageTag acquire_tag;
bool operator()(const ReadState &read_access) const; // Read access predicate
bool operator()(const ResourceAccessState &access) const; // Write access predicate
};
friend WaitAcquirePredicate;
template <typename Predicate>
bool ApplyPredicatedWait(Predicate &predicate);
bool FirstAccessInTagRange(const ResourceUsageRange &tag_range) const;
void OffsetTag(ResourceUsageTag offset);
ResourceAccessState();
bool HasPendingState() const { return (0 != pending_layout_transition) || (last_write && last_write->HasPendingState()); }
bool HasWriteOp() const { return last_write.has_value(); }
SyncStageAccessIndex LastWriteOp() const { return last_write.has_value() ? last_write->Index() : SYNC_ACCESS_INDEX_NONE; }
bool IsLastWriteOp(SyncStageAccessIndex usage_index) const { return LastWriteOp() == usage_index; }
ResourceUsageTag LastWriteTag() const { return last_write.has_value() ? last_write->Tag() : ResourceUsageTag(0); }
bool operator==(const ResourceAccessState &rhs) const {
const bool write_same = (read_execution_barriers == rhs.read_execution_barriers) &&
(input_attachment_read == rhs.input_attachment_read) && (last_write == rhs.last_write);
const bool read_write_same = write_same && (last_read_stages == rhs.last_read_stages) && (last_reads == rhs.last_reads);
const bool same = read_write_same && (first_accesses_ == rhs.first_accesses_) &&
(first_read_stages_ == rhs.first_read_stages_) &&
(first_write_layout_ordering_ == rhs.first_write_layout_ordering_);
return same;
}
bool operator!=(const ResourceAccessState &rhs) const { return !(*this == rhs); }
VkPipelineStageFlags2KHR GetReadBarriers(const SyncStageAccessFlags &usage) const;
SyncStageAccessFlags GetWriteBarriers() const {
return last_write.has_value() ? last_write->Barriers() : SyncStageAccessFlags();
}
void SetQueueId(QueueId id);
bool IsWriteBarrierHazard(QueueId queue_id, VkPipelineStageFlags2KHR src_exec_scope,
const SyncStageAccessFlags &src_access_scope) const;
bool WriteInSourceScopeOrChain(VkPipelineStageFlags2KHR src_exec_scope, SyncStageAccessFlags src_access_scope) const;
bool WriteInQueueSourceScopeOrChain(QueueId queue, VkPipelineStageFlags2KHR src_exec_scope,
const SyncStageAccessFlags &src_access_scope) const;
bool WriteInEventScope(VkPipelineStageFlags2KHR src_exec_scope, const SyncStageAccessFlags &src_access_scope,
QueueId scope_queue, ResourceUsageTag scope_tag) const;
struct UntaggedScopeOps {
bool WriteInScope(const SyncBarrier &barrier, const ResourceAccessState &access) const {
return access.WriteInSourceScopeOrChain(barrier.src_exec_scope.exec_scope, barrier.src_access_scope);
}
bool ReadInScope(const SyncBarrier &barrier, const ReadState &read_state) const {
return read_state.ReadInScopeOrChain(barrier.src_exec_scope.exec_scope);
}
};
struct QueueScopeOps {
bool WriteInScope(const SyncBarrier &barrier, const ResourceAccessState &access) const {
return access.WriteInQueueSourceScopeOrChain(queue, barrier.src_exec_scope.exec_scope, barrier.src_access_scope);
}
bool ReadInScope(const SyncBarrier &barrier, const ReadState &read_state) const {
return read_state.ReadInQueueScopeOrChain(queue, barrier.src_exec_scope.exec_scope);
}
QueueScopeOps(QueueId scope_queue) : queue(scope_queue) {}
QueueId queue;
};
struct EventScopeOps {
bool WriteInScope(const SyncBarrier &barrier, const ResourceAccessState &access) const {
return access.WriteInEventScope(barrier.src_exec_scope.exec_scope, barrier.src_access_scope, scope_queue, scope_tag);
}
bool ReadInScope(const SyncBarrier &barrier, const ReadState &read_state) const {
return read_state.ReadInEventScope(barrier.src_exec_scope.exec_scope, scope_queue, scope_tag);
}
EventScopeOps(QueueId qid, ResourceUsageTag event_tag) : scope_queue(qid), scope_tag(event_tag) {}
QueueId scope_queue;
ResourceUsageTag scope_tag;
};
void Normalize();
void GatherReferencedTags(ResourceUsageTagSet &used) const;
private:
static constexpr VkPipelineStageFlags2KHR kInvalidAttachmentStage = ~VkPipelineStageFlags2KHR(0);
bool IsRAWHazard(const SyncStageAccessInfoType &usage_info) const;
bool WriteInScope(const SyncStageAccessFlags &src_access_scope) const;
// Apply ordering scope to write hazard detection
bool ReadInSourceScopeOrChain(VkPipelineStageFlags2KHR src_exec_scope) const {
return (0 != (src_exec_scope & (last_read_stages | read_execution_barriers)));
}
static bool IsReadHazard(VkPipelineStageFlags2KHR stage_mask, const VkPipelineStageFlags2KHR barriers) {
return stage_mask != (stage_mask & barriers);
}
bool IsReadHazard(VkPipelineStageFlags2KHR stage_mask, const ReadState &read_access) const {
return IsReadHazard(stage_mask, read_access.barriers);
}
VkPipelineStageFlags2 GetOrderedStages(QueueId queue_id, const OrderingBarrier &ordering) const;
void UpdateFirst(ResourceUsageTag tag, const SyncStageAccessInfoType &usage_info, SyncOrdering ordering_rule);
void TouchupFirstForLayoutTransition(ResourceUsageTag tag, const OrderingBarrier &layout_ordering);
void MergePending(const ResourceAccessState &other);
void MergeReads(const ResourceAccessState &other);
static const OrderingBarrier &GetOrderingRules(SyncOrdering ordering_enum) {
return kOrderingRules[static_cast<size_t>(ordering_enum)];
}
// TODO: Add a NONE (zero) enum to SyncStageAccessFlags for input_attachment_read and last_write
// With reads, each must be "safe" relative to it's prior write, so we need only
// save the most recent write operation (as anything *transitively* unsafe would arleady
// be included
// SyncStageAccessFlags write_barriers; // union of applicable barrier masks since last write
// VkPipelineStageFlags2KHR write_dependency_chain; // intiially zero, but accumulating the dstStages of barriers if they
// chain. ResourceUsageTag write_tag; QueueId write_queue;
std::optional<ResourceAccessWriteState> last_write; // only the most recent write
VkPipelineStageFlags2KHR last_read_stages;
VkPipelineStageFlags2KHR read_execution_barriers;
using ReadStates = small_vector<ReadState, 3, uint32_t>;
ReadStates last_reads;
// TODO Input Attachment cleanup for multiple reads in a given stage
// Tracks whether the fragment shader read is input attachment read
bool input_attachment_read;
// Not part of the write state, logically. Can exist when !last_write
// Pending execution state to support independent parallel barriers
bool pending_layout_transition;
FirstAccesses first_accesses_;
VkPipelineStageFlags2KHR first_read_stages_;
OrderingBarrier first_write_layout_ordering_;
bool first_access_closed_;
static OrderingBarriers kOrderingRules;
};
using ResourceAccessStateFunction = std::function<void(ResourceAccessState *)>;
using ResourceAddress = VkDeviceSize;
using ResourceAccessRangeMap = sparse_container::range_map<ResourceAddress, ResourceAccessState>;
using ResourceAccessRange = typename ResourceAccessRangeMap::key_type;
using ResourceRangeMergeIterator = sparse_container::parallel_iterator<ResourceAccessRangeMap, const ResourceAccessRangeMap>;
struct FenceSyncState {
std::shared_ptr<const FENCE_STATE> fence;
ResourceUsageTag tag;
QueueId queue_id;
AcquiredImage acquired; // Iff queue == invalid and acquired.image valid.
FenceSyncState();
FenceSyncState(const FenceSyncState &other) = default;
FenceSyncState(FenceSyncState &&other) = default;
FenceSyncState &operator=(const FenceSyncState &other) = default;
FenceSyncState &operator=(FenceSyncState &&other) = default;
FenceSyncState(const std::shared_ptr<const FENCE_STATE> &fence_, QueueId queue_id_, ResourceUsageTag tag_);
FenceSyncState(const std::shared_ptr<const FENCE_STATE> &fence_, const PresentedImage &image, ResourceUsageTag tag_);
};
class AttachmentViewGen {
public:
enum Gen { kViewSubresource = 0, kRenderArea = 1, kDepthOnlyRenderArea = 2, kStencilOnlyRenderArea = 3, kGenSize = 4 };
AttachmentViewGen(const syncval_state::ImageViewState *image_view, const VkOffset3D &offset, const VkExtent3D &extent);
AttachmentViewGen(const AttachmentViewGen &other) = default;
AttachmentViewGen(AttachmentViewGen &&other) = default;
const syncval_state::ImageViewState *GetViewState() const { return view_; }
const std::optional<ImageRangeGen> &GetRangeGen(Gen type) const;
bool IsValid() const { return gen_store_[Gen::kViewSubresource].has_value(); }
Gen GetDepthStencilRenderAreaGenType(bool depth_op, bool stencil_op) const;
private:
const syncval_state::ImageViewState *view_ = nullptr;
VkImageAspectFlags view_mask_ = 0U;
std::array<std::optional<ImageRangeGen>, Gen::kGenSize> gen_store_;
};
using AttachmentViewGenVector = std::vector<AttachmentViewGen>;
using SyncMemoryBarrier = SyncBarrier;
struct SyncBufferMemoryBarrier {
using Buffer = std::shared_ptr<const BUFFER_STATE>;
Buffer buffer;
SyncBarrier barrier;
ResourceAccessRange range;
bool IsLayoutTransition() const { return false; }
const ResourceAccessRange &Range() const { return range; };
const BUFFER_STATE *GetState() const { return buffer.get(); }
SyncBufferMemoryBarrier(const Buffer &buffer_, const SyncBarrier &barrier_, const ResourceAccessRange &range_)
: buffer(buffer_), barrier(barrier_), range(range_) {}
SyncBufferMemoryBarrier() = default;
};
struct SyncImageMemoryBarrier {
using ImageState = syncval_state::ImageState;
using Image = std::shared_ptr<const ImageState>;
Image image;
uint32_t index;
SyncBarrier barrier;
VkImageLayout old_layout;
VkImageLayout new_layout;
VkImageSubresourceRange range;
bool IsLayoutTransition() const { return old_layout != new_layout; }
const VkImageSubresourceRange &Range() const { return range; };
const ImageState *GetState() const { return image.get(); }
SyncImageMemoryBarrier(const Image &image_, uint32_t index_, const SyncBarrier &barrier_, VkImageLayout old_layout_,
VkImageLayout new_layout_, const VkImageSubresourceRange &subresource_range_)
: image(image_),
index(index_),
barrier(barrier_),
old_layout(old_layout_),
new_layout(new_layout_),
range(subresource_range_) {}
SyncImageMemoryBarrier() = default;
};
template <typename SubpassNode>
struct SubpassBarrierTrackback {
std::vector<SyncBarrier> barriers;
const SubpassNode *source_subpass = nullptr;
SubpassBarrierTrackback() = default;
SubpassBarrierTrackback(const SubpassBarrierTrackback &) = default;
SubpassBarrierTrackback(const SubpassNode *source_subpass_, VkQueueFlags queue_flags_,
const std::vector<const VkSubpassDependency2 *> &subpass_dependencies_)
: barriers(), source_subpass(source_subpass_) {
barriers.reserve(subpass_dependencies_.size());
for (const VkSubpassDependency2 *dependency : subpass_dependencies_) {
assert(dependency);
barriers.emplace_back(queue_flags_, *dependency);
}
}
SubpassBarrierTrackback(const SubpassNode *source_subpass_, const SyncBarrier &barrier_)
: barriers(1, barrier_), source_subpass(source_subpass_) {}
SubpassBarrierTrackback &operator=(const SubpassBarrierTrackback &) = default;
};
class SyncOpBase {
public:
SyncOpBase() : command_(vvl::Func::Empty) {}
SyncOpBase(vvl::Func command) : command_(command) {}
virtual ~SyncOpBase() = default;
const char *CmdName() const { return vvl::String(command_); }
vvl::Func Cmd() const { return command_; }
virtual bool Validate(const CommandBufferAccessContext &cb_context) const = 0;
virtual ResourceUsageTag Record(CommandBufferAccessContext *cb_context) = 0;
virtual bool ReplayValidate(ReplayState &replay, ResourceUsageTag recorded_tag) const = 0;
virtual void ReplayRecord(CommandExecutionContext &exec_context, ResourceUsageTag exec_tag) const = 0;
protected:
// Only non-null and valid for SyncOps within a render pass instance WIP -- think about how to manage for non RPI calls within
// RPI and 2ndarys...
uint32_t subpass_ = VK_SUBPASS_EXTERNAL;
vvl::Func command_;
};
class SyncOpBarriers : public SyncOpBase {
protected:
template <typename Barriers, typename FunctorFactory>
static void ApplyBarriers(const Barriers &barriers, const FunctorFactory &factory, QueueId queue_id, ResourceUsageTag tag,
AccessContext *context);
template <typename Barriers, typename FunctorFactory>
static void ApplyGlobalBarriers(const Barriers &barriers, const FunctorFactory &factory, QueueId queue_id, ResourceUsageTag tag,
AccessContext *access_context);
SyncOpBarriers(vvl::Func command, const SyncValidator &sync_state, VkQueueFlags queue_flags, VkPipelineStageFlags srcStageMask,
VkPipelineStageFlags dstStageMask, VkDependencyFlags dependencyFlags, uint32_t memoryBarrierCount,
const VkMemoryBarrier *pMemoryBarriers, uint32_t bufferMemoryBarrierCount,
const VkBufferMemoryBarrier *pBufferMemoryBarriers, uint32_t imageMemoryBarrierCount,
const VkImageMemoryBarrier *pImageMemoryBarriers);
SyncOpBarriers(vvl::Func command, const SyncValidator &sync_state, VkQueueFlags queue_flags, uint32_t event_count,
const VkDependencyInfoKHR *pDependencyInfo);
~SyncOpBarriers() override = default;
protected:
struct BarrierSet {
using ImageState = syncval_state::ImageState;
VkDependencyFlags dependency_flags;
SyncExecScope src_exec_scope;
SyncExecScope dst_exec_scope;
std::vector<SyncMemoryBarrier> memory_barriers;
std::vector<SyncBufferMemoryBarrier> buffer_memory_barriers;
std::vector<SyncImageMemoryBarrier> image_memory_barriers;
bool single_exec_scope;
void MakeMemoryBarriers(const SyncExecScope &src, const SyncExecScope &dst, VkDependencyFlags dependencyFlags,
uint32_t memoryBarrierCount, const VkMemoryBarrier *pMemoryBarriers);
void MakeBufferMemoryBarriers(const SyncValidator &sync_state, const SyncExecScope &src, const SyncExecScope &dst,
VkDependencyFlags dependencyFlags, uint32_t bufferMemoryBarrierCount,
const VkBufferMemoryBarrier *pBufferMemoryBarriers);
void MakeImageMemoryBarriers(const SyncValidator &sync_state, const SyncExecScope &src, const SyncExecScope &dst,
VkDependencyFlags dependencyFlags, uint32_t imageMemoryBarrierCount,
const VkImageMemoryBarrier *pImageMemoryBarriers);
void MakeMemoryBarriers(VkQueueFlags queue_flags, VkDependencyFlags dependency_flags, uint32_t barrier_count,
const VkMemoryBarrier2 *barriers);
void MakeBufferMemoryBarriers(const SyncValidator &sync_state, VkQueueFlags queue_flags, VkDependencyFlags dependency_flags,
uint32_t barrier_count, const VkBufferMemoryBarrier2 *barriers);
void MakeImageMemoryBarriers(const SyncValidator &sync_state, VkQueueFlags queue_flags, VkDependencyFlags dependency_flags,
uint32_t barrier_count, const VkImageMemoryBarrier2 *barriers);
};
std::vector<BarrierSet> barriers_;
};
class SyncOpPipelineBarrier : public SyncOpBarriers {
public:
SyncOpPipelineBarrier(vvl::Func command, const SyncValidator &sync_state, VkQueueFlags queue_flags,
VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, VkDependencyFlags dependencyFlags,
uint32_t memoryBarrierCount, const VkMemoryBarrier *pMemoryBarriers, uint32_t bufferMemoryBarrierCount,
const VkBufferMemoryBarrier *pBufferMemoryBarriers, uint32_t imageMemoryBarrierCount,
const VkImageMemoryBarrier *pImageMemoryBarriers);
SyncOpPipelineBarrier(vvl::Func command, const SyncValidator &sync_state, VkQueueFlags queue_flags,
const VkDependencyInfoKHR &pDependencyInfo);
~SyncOpPipelineBarrier() override = default;
bool Validate(const CommandBufferAccessContext &cb_context) const override;
ResourceUsageTag Record(CommandBufferAccessContext *cb_context) override;
bool ReplayValidate(ReplayState &replay, ResourceUsageTag recorded_tag) const override;
void ReplayRecord(CommandExecutionContext &exec_context, ResourceUsageTag exec_tag) const override;
};
class SyncOpWaitEvents : public SyncOpBarriers {
public:
SyncOpWaitEvents(vvl::Func command, const SyncValidator &sync_state, VkQueueFlags queue_flags, uint32_t eventCount,
const VkEvent *pEvents, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask,
uint32_t memoryBarrierCount, const VkMemoryBarrier *pMemoryBarriers, uint32_t bufferMemoryBarrierCount,
const VkBufferMemoryBarrier *pBufferMemoryBarriers, uint32_t imageMemoryBarrierCount,
const VkImageMemoryBarrier *pImageMemoryBarriers);
SyncOpWaitEvents(vvl::Func command, const SyncValidator &sync_state, VkQueueFlags queue_flags, uint32_t eventCount,
const VkEvent *pEvents, const VkDependencyInfoKHR *pDependencyInfo);
~SyncOpWaitEvents() override = default;
bool Validate(const CommandBufferAccessContext &cb_context) const override;
ResourceUsageTag Record(CommandBufferAccessContext *cb_context) override;
bool ReplayValidate(ReplayState &replay, ResourceUsageTag recorded_tag) const override;
void ReplayRecord(CommandExecutionContext &exec_context, ResourceUsageTag exec_tag) const override;
protected:
static const char *const kIgnored;
bool DoValidate(const CommandExecutionContext &ex_context, const ResourceUsageTag base_tag) const;
void DoRecord(CommandExecutionContext &ex_context, const ResourceUsageTag base_tag) const;
// TODO PHASE2 This is the wrong thing to use for "replay".. as the event state will have moved on since the record
// TODO PHASE2 May need to capture by value w.r.t. "first use" or build up in calling/enqueue context through replay.
std::vector<std::shared_ptr<const EVENT_STATE>> events_;
void MakeEventsList(const SyncValidator &sync_state, uint32_t event_count, const VkEvent *events);
};
class SyncOpResetEvent : public SyncOpBase {
public:
SyncOpResetEvent(vvl::Func command, const SyncValidator &sync_state, VkQueueFlags queue_flags, VkEvent event,
VkPipelineStageFlags2KHR stageMask);
~SyncOpResetEvent() override = default;
bool Validate(const CommandBufferAccessContext &cb_context) const override;
ResourceUsageTag Record(CommandBufferAccessContext *cb_context) override;
bool ReplayValidate(ReplayState &replay, ResourceUsageTag recorded_tag) const override;
void ReplayRecord(CommandExecutionContext &exec_context, ResourceUsageTag exec_tag) const override;
private:
bool DoValidate(const CommandExecutionContext &ex_context, const ResourceUsageTag base_tag) const;
std::shared_ptr<const EVENT_STATE> event_;
SyncExecScope exec_scope_;
};
class SyncOpSetEvent : public SyncOpBase {
public:
SyncOpSetEvent(vvl::Func command, const SyncValidator &sync_state, VkQueueFlags queue_flags, VkEvent event,
VkPipelineStageFlags2KHR stageMask, const AccessContext *access_context);
SyncOpSetEvent(vvl::Func command, const SyncValidator &sync_state, VkQueueFlags queue_flags, VkEvent event,
const VkDependencyInfoKHR &dep_info, const AccessContext *access_context);
~SyncOpSetEvent() override = default;
bool Validate(const CommandBufferAccessContext &cb_context) const override;
ResourceUsageTag Record(CommandBufferAccessContext *cb_context) override;
bool ReplayValidate(ReplayState &replay, ResourceUsageTag recorded_tag) const override;
void ReplayRecord(CommandExecutionContext &exec_context, ResourceUsageTag exec_tag) const override;
private:
bool DoValidate(const CommandExecutionContext &ex_context, const ResourceUsageTag base_tag) const;
void DoRecord(QueueId queue_id, ResourceUsageTag recorded_tag, const std::shared_ptr<const AccessContext> &access_context,
SyncEventsContext *events_context) const;
std::shared_ptr<const EVENT_STATE> event_;
// The Access context of the command buffer at record set event time.
std::shared_ptr<const AccessContext> recorded_context_;
SyncExecScope src_exec_scope_;
// Note that the dep info is *not* dehandled, but retained for comparison with a future WaitEvents2
std::shared_ptr<safe_VkDependencyInfo> dep_info_;
};
class SyncOpBeginRenderPass : public SyncOpBase {
public:
SyncOpBeginRenderPass(vvl::Func command, const SyncValidator &sync_state, const VkRenderPassBeginInfo *pRenderPassBegin,
const VkSubpassBeginInfo *pSubpassBeginInfo);
~SyncOpBeginRenderPass() override = default;
bool Validate(const CommandBufferAccessContext &cb_context) const override;
ResourceUsageTag Record(CommandBufferAccessContext *cb_context) override;
bool ReplayValidate(ReplayState &replay, ResourceUsageTag recorded_tag) const override;
void ReplayRecord(CommandExecutionContext &exec_context, ResourceUsageTag exec_tag) const override;
const RenderPassAccessContext *GetRenderPassAccessContext() const { return rp_context_; }
protected:
safe_VkRenderPassBeginInfo renderpass_begin_info_;
safe_VkSubpassBeginInfo subpass_begin_info_;
std::vector<std::shared_ptr<const IMAGE_VIEW_STATE>> shared_attachments_;
std::vector<const syncval_state::ImageViewState *> attachments_;
std::shared_ptr<const RENDER_PASS_STATE> rp_state_;
const RenderPassAccessContext *rp_context_;
};
class SyncOpNextSubpass : public SyncOpBase {
public:
SyncOpNextSubpass(vvl::Func command, const SyncValidator &sync_state, const VkSubpassBeginInfo *pSubpassBeginInfo,
const VkSubpassEndInfo *pSubpassEndInfo);
~SyncOpNextSubpass() override = default;
bool Validate(const CommandBufferAccessContext &cb_context) const override;
ResourceUsageTag Record(CommandBufferAccessContext *cb_context) override;
bool ReplayValidate(ReplayState &replay, ResourceUsageTag recorded_tag) const override;
void ReplayRecord(CommandExecutionContext &exec_context, ResourceUsageTag exec_tag) const override;
protected:
safe_VkSubpassBeginInfo subpass_begin_info_;
safe_VkSubpassEndInfo subpass_end_info_;
};
class SyncOpEndRenderPass : public SyncOpBase {
public:
SyncOpEndRenderPass(vvl::Func command, const SyncValidator &sync_state, const VkSubpassEndInfo *pSubpassEndInfo);
~SyncOpEndRenderPass() override = default;
bool Validate(const CommandBufferAccessContext &cb_context) const override;
ResourceUsageTag Record(CommandBufferAccessContext *cb_context) override;
bool ReplayValidate(ReplayState &replay, ResourceUsageTag recorded_tag) const override;
void ReplayRecord(CommandExecutionContext &exec_context, ResourceUsageTag exec_tag) const override;
protected:
safe_VkSubpassEndInfo subpass_end_info_;
};
class AccessContext {
public:
using ImageState = syncval_state::ImageState;
using ImageViewState = syncval_state::ImageViewState;
enum DetectOptions : uint32_t {
kDetectPrevious = 1U << 0,
kDetectAsync = 1U << 1,
kDetectAll = (kDetectPrevious | kDetectAsync)
};
using TrackBack = SubpassBarrierTrackback<AccessContext>;
template <typename Detector, typename RangeGen>
HazardResult DetectHazard(Detector &detector, RangeGen &range_gen, DetectOptions options) const;
template <typename Detector, typename RangeGen>
HazardResult DetectHazard(Detector &detector, const RangeGen &range_gen, DetectOptions options) const {
RangeGen mutable_gen(range_gen);
return DetectHazard<Detector, RangeGen>(detector, mutable_gen, options);
}
HazardResult DetectHazard(const BUFFER_STATE &buffer, SyncStageAccessIndex usage_index, const ResourceAccessRange &range) const;
HazardResult DetectHazard(const ImageState &image, SyncStageAccessIndex current_usage,
const VkImageSubresourceLayers &subresource, const VkOffset3D &offset, const VkExtent3D &extent,
bool is_depth_sliced) const;
template <typename Detector>
HazardResult DetectHazard(Detector &detector, const AttachmentViewGen &view_gen, AttachmentViewGen::Gen gen_type,
DetectOptions options) const;
template <typename Detector>
HazardResult DetectHazard(Detector &detector, const ImageState &image, const VkImageSubresourceRange &subresource_range,
const VkOffset3D &offset, const VkExtent3D &extent, bool is_depth_sliced,
DetectOptions options) const;
template <typename Detector>
HazardResult DetectHazard(Detector &detector, const ImageState &image, const VkImageSubresourceRange &subresource_range,
bool is_depth_sliced, DetectOptions options) const;
HazardResult DetectHazard(const ImageState &image, SyncStageAccessIndex current_usage,
const VkImageSubresourceRange &subresource_range, bool is_depth_sliced) const;
HazardResult DetectHazard(const ImageViewState &image_view, SyncStageAccessIndex current_usage) const;
HazardResult DetectHazard(const ImageViewState &image_view, SyncStageAccessIndex current_usage, SyncOrdering ordering_rule,
const VkOffset3D &offset, const VkExtent3D &extent) const;
HazardResult DetectHazard(const AttachmentViewGen &view_gen, AttachmentViewGen::Gen gen_type,
SyncStageAccessIndex current_usage, SyncOrdering ordering_rule) const;
HazardResult DetectHazard(const ImageState &image, SyncStageAccessIndex current_usage,
const VkImageSubresourceRange &subresource_range, SyncOrdering ordering_rule,
const VkOffset3D &offset, const VkExtent3D &extent, bool is_depth_sliced) const;
HazardResult DetectImageBarrierHazard(const ImageState &image, const VkImageSubresourceRange &subresource_range,
VkPipelineStageFlags2KHR src_exec_scope, const SyncStageAccessFlags &src_access_scope,
QueueId queue_id, const SyncEventState &sync_event, DetectOptions options) const;
HazardResult DetectImageBarrierHazard(const AttachmentViewGen &attachment_view, const SyncBarrier &barrier,
DetectOptions options) const;
HazardResult DetectImageBarrierHazard(const ImageState &image, VkPipelineStageFlags2KHR src_exec_scope,
const SyncStageAccessFlags &src_access_scope,
const VkImageSubresourceRange &subresource_range, DetectOptions options) const;
HazardResult DetectImageBarrierHazard(const SyncImageMemoryBarrier &image_barrier) const;
HazardResult DetectSubpassTransitionHazard(const TrackBack &track_back, const AttachmentViewGen &attach_view) const;
void RecordLayoutTransitions(const RENDER_PASS_STATE &rp_state, uint32_t subpass,
const AttachmentViewGenVector &attachment_views, ResourceUsageTag tag);
HazardResult DetectFirstUseHazard(QueueId queue_id, const ResourceUsageRange &tag_range,
const AccessContext &access_context) const;
const TrackBack &GetDstExternalTrackBack() const { return dst_external_; }
void Reset() {
prev_.clear();
prev_by_subpass_.clear();
async_.clear();
src_external_ = nullptr;
dst_external_ = TrackBack();
start_tag_ = ResourceUsageTag();
access_state_map_.clear();
}
// Follow the context previous to access the access state, supporting "lazy" import into the context. Not intended for
// subpass layout transition, as the pending state handling is more complex
// TODO: See if returning the lower_bound would be useful from a performance POV -- look at the lower_bound overhead
// Would need to add a "hint" overload to parallel_iterator::invalidate_[AB] call, if so.
template <typename BarrierAction>
void ResolvePreviousAccessStack(const ResourceAccessRange &range, ResourceAccessRangeMap *descent_map,
const ResourceAccessState *infill_state, const BarrierAction &previous_barrie) const;
void ResolvePreviousAccess(const ResourceAccessRange &range, ResourceAccessRangeMap *descent_map,
const ResourceAccessState *infill_state,
const ResourceAccessStateFunction *previous_barrier = nullptr) const;
void ResolvePreviousAccesses();
template <typename BarrierAction>
void ResolveAccessRange(const AttachmentViewGen &view_gen, AttachmentViewGen::Gen gen_type, BarrierAction &barrier_action,
ResourceAccessRangeMap *descent_map, const ResourceAccessState *infill_state) const;
template <typename BarrierAction>
void ResolveAccessRange(const ResourceAccessRange &range, BarrierAction &barrier_action, ResourceAccessRangeMap *resolve_map,
const ResourceAccessState *infill_state, bool recur_to_infill = true) const;
template <typename ResolveOp>
void ResolveFromContext(ResolveOp &&resolve_op, const AccessContext &from_context,
const ResourceAccessState *infill_state = nullptr, bool recur_to_infill = false);
template <typename ResolveOp, typename RangeGenerator>
void ResolveFromContext(ResolveOp &&resolve_op, const AccessContext &from_context, RangeGenerator range_gen,
const ResourceAccessState *infill_state = nullptr, bool recur_to_infill = false);
void UpdateAccessState(const BUFFER_STATE &buffer, SyncStageAccessIndex current_usage, SyncOrdering ordering_rule,
const ResourceAccessRange &range, ResourceUsageTag tag);
void UpdateAccessState(const ImageState &image, SyncStageAccessIndex current_usage, SyncOrdering ordering_rule,
const VkImageSubresourceRange &subresource_range, const ResourceUsageTag &tag);
void UpdateAccessState(const ImageState &image, SyncStageAccessIndex current_usage, SyncOrdering ordering_rule,
const VkImageSubresourceRange &subresource_range, const VkOffset3D &offset, const VkExtent3D &extent,
ResourceUsageTag tag);
void UpdateAccessState(const AttachmentViewGen &view_gen, AttachmentViewGen::Gen gen_type, SyncStageAccessIndex current_usage,
SyncOrdering ordering_rule, ResourceUsageTag tag);
void UpdateAccessState(const ImageViewState &image_view, SyncStageAccessIndex current_usage, SyncOrdering ordering_rule,
const VkOffset3D &offset, const VkExtent3D &extent, ResourceUsageTag tag);
void UpdateAccessState(const ImageViewState &image_view, SyncStageAccessIndex current_usage, SyncOrdering ordering_rule,
ResourceUsageTag tag);
void UpdateAccessState(const ImageState &image, SyncStageAccessIndex current_usage, SyncOrdering ordering_rule,
const VkImageSubresourceLayers &subresource, const VkOffset3D &offset, const VkExtent3D &extent,
ResourceUsageTag tag);
void UpdateAccessState(const ImageRangeGen &range_gen, SyncStageAccessIndex current_usage, SyncOrdering ordering_rule,
ResourceUsageTag tag);
void UpdateAccessState(ImageRangeGen &range_gen, SyncStageAccessIndex current_usage, SyncOrdering ordering_rule,
ResourceUsageTag tag);
void UpdateAttachmentResolveAccess(const RENDER_PASS_STATE &rp_state, const AttachmentViewGenVector &attachment_views,
uint32_t subpass, ResourceUsageTag tag);
void UpdateAttachmentStoreAccess(const RENDER_PASS_STATE &rp_state, const AttachmentViewGenVector &attachment_views,
uint32_t subpass, ResourceUsageTag tag);
void ResolveChildContexts(const std::vector<AccessContext> &contexts);
void ImportAsyncContexts(const AccessContext &from);
void ClearAsyncContexts() { async_.clear(); }
template <typename Action>
void ApplyUpdateAction(const AttachmentViewGen &view_gen, AttachmentViewGen::Gen gen_type, const Action &action);
template <typename Action>
void ApplyToContext(const Action &barrier_action);
AccessContext(uint32_t subpass, VkQueueFlags queue_flags, const std::vector<SubpassDependencyGraphNode> &dependencies,
const std::vector<AccessContext> &contexts, const AccessContext *external_context);
AccessContext() { Reset(); }
AccessContext(const AccessContext &copy_from) = default;
void Trim();
void AddReferencedTags(ResourceUsageTagSet &referenced) const;
ResourceAccessRangeMap &GetAccessStateMap() { return access_state_map_; }
const ResourceAccessRangeMap &GetAccessStateMap() const { return access_state_map_; }
const TrackBack *GetTrackBackFromSubpass(uint32_t subpass) const {
if (subpass == VK_SUBPASS_EXTERNAL) {
return src_external_;
} else {
assert(subpass < prev_by_subpass_.size());
return prev_by_subpass_[subpass];
}
}
bool ValidateLayoutTransitions(const CommandExecutionContext &ex_context, const RENDER_PASS_STATE &rp_state,
const VkRect2D &render_area, uint32_t subpass, const AttachmentViewGenVector &attachment_views,
vvl::Func command) const;
bool ValidateLoadOperation(const CommandExecutionContext &ex_context, const RENDER_PASS_STATE &rp_state,
const VkRect2D &render_area, uint32_t subpass, const AttachmentViewGenVector &attachment_views,
vvl::Func command) const;
bool ValidateStoreOperation(const CommandExecutionContext &ex_context, const RENDER_PASS_STATE &rp_state,
const VkRect2D &render_area, uint32_t subpass, const AttachmentViewGenVector &attachment_views,
vvl::Func command) const;
bool ValidateResolveOperations(const CommandExecutionContext &ex_context, const RENDER_PASS_STATE &rp_state,
const VkRect2D &render_area, const AttachmentViewGenVector &attachment_views, vvl::Func command,
uint32_t subpass) const;
void SetStartTag(ResourceUsageTag tag) { start_tag_ = tag; }
ResourceUsageTag StartTag() const { return start_tag_; }
template <typename Action>
void ForAll(Action &&action);
template <typename Action>
void ConstForAll(Action &&action) const;
template <typename Predicate>
void EraseIf(Predicate &&pred);
// For use during queue submit building up the QueueBatchContext AccessContext for validation, otherwise clear.
void AddAsyncContext(const AccessContext *context, ResourceUsageTag tag);
class AsyncReference {
public:
AsyncReference(const AccessContext &async_context, ResourceUsageTag async_tag)
: context_(&async_context), tag_(async_tag) {}
const AccessContext &Context() const { return *context_; }
// For RenderPass time validation this is "start tag", for QueueSubmit, this is the earliest
// unsynchronized tag for the Queue being tested against (max synchrononous + 1, perhaps)
ResourceUsageTag StartTag() const;
protected:
const AccessContext *context_;
ResourceUsageTag tag_; // Start of open ended asynchronous range
};
private:
template <typename Detector>
HazardResult DetectHazard(Detector &detector, const ResourceAccessRange &range, DetectOptions options) const;
template <typename Detector>
HazardResult DetectAsyncHazard(const Detector &detector, const ResourceAccessRange &range, ResourceUsageTag async_tag) const;
template <typename Detector>
HazardResult DetectPreviousHazard(Detector &detector, const ResourceAccessRange &range) const;
ResourceAccessRangeMap access_state_map_;
std::vector<TrackBack> prev_;
std::vector<TrackBack *> prev_by_subpass_;
// These contexts *must* have the same lifespan as this context, or be cleared, before the referenced contexts can expire
std::vector<AsyncReference> async_;
TrackBack *src_external_;
TrackBack dst_external_;
ResourceUsageTag start_tag_;
};
struct SyncEventState {
enum IgnoreReason { NotIgnored = 0, ResetWaitRace, Reset2WaitRace, SetRace, MissingStageBits, SetVsWait2, MissingSetEvent };
using EventPointer = std::shared_ptr<const EVENT_STATE>;
using ScopeMap = ResourceAccessRangeMap;
EventPointer event;
vvl::Func last_command; // Only Event commands are valid here.
ResourceUsageTag last_command_tag; // Needed to filter replay validation
vvl::Func unsynchronized_set;
VkPipelineStageFlags2KHR barriers;
SyncExecScope scope;
ResourceUsageTag first_scope_tag;
bool destroyed;
std::shared_ptr<const AccessContext> first_scope;
SyncEventState()
: event(),
last_command(vvl::Func::Empty),
last_command_tag(0),
unsynchronized_set(vvl::Func::Empty),
barriers(0U),
scope(),
first_scope_tag(),
destroyed(true) {}
SyncEventState(const SyncEventState &) = default;
SyncEventState(SyncEventState &&) = default;
SyncEventState(const SyncEventState::EventPointer &event_state) : SyncEventState() {
event = event_state;
destroyed = (event.get() == nullptr) || event_state->Destroyed();
}
void ResetFirstScope();
const ScopeMap &FirstScope() const { return first_scope->GetAccessStateMap(); }
IgnoreReason IsIgnoredByWait(vvl::Func command, VkPipelineStageFlags2KHR srcStageMask) const;
bool HasBarrier(VkPipelineStageFlags2KHR stageMask, VkPipelineStageFlags2KHR exec_scope) const;
void AddReferencedTags(ResourceUsageTagSet &referenced) const;
};
class SyncEventsContext {
public:
using Map = vvl::unordered_map<const EVENT_STATE *, std::shared_ptr<SyncEventState>>;
using iterator = Map::iterator;
using const_iterator = Map::const_iterator;
SyncEventState *GetFromShared(const SyncEventState::EventPointer &event_state) {
const auto find_it = map_.find(event_state.get());
if (find_it == map_.end()) {
if (!event_state.get()) return nullptr;
const auto *event_plain_ptr = event_state.get();
auto sync_state = std::make_shared<SyncEventState>(event_state);
auto insert_pair = map_.emplace(event_plain_ptr, sync_state);
return insert_pair.first->second.get();
}
return find_it->second.get();
}
const SyncEventState *Get(const EVENT_STATE *event_state) const {
const auto find_it = map_.find(event_state);
if (find_it == map_.end()) {
return nullptr;
}
return find_it->second.get();
}
const SyncEventState *Get(const SyncEventState::EventPointer &event_state) const { return Get(event_state.get()); }
void ApplyBarrier(const SyncExecScope &src, const SyncExecScope &dst, ResourceUsageTag tag);
void ApplyTaggedWait(VkQueueFlags queue_flags, ResourceUsageTag tag);
void Destroy(const EVENT_STATE *event_state) {
auto sync_it = map_.find(event_state);
if (sync_it != map_.end()) {
sync_it->second->destroyed = true;
map_.erase(sync_it);
}
}
void Clear() { map_.clear(); }
SyncEventsContext &DeepCopy(const SyncEventsContext &from);
void AddReferencedTags(ResourceUsageTagSet &referenced) const;
private:
Map map_;
};
class RenderPassAccessContext {
public:
static AttachmentViewGenVector CreateAttachmentViewGen(
const VkRect2D &render_area, const std::vector<const syncval_state::ImageViewState *> &attachment_views);
RenderPassAccessContext() : rp_state_(nullptr), render_area_(VkRect2D()), current_subpass_(0) {}
RenderPassAccessContext(const RENDER_PASS_STATE &rp_state, const VkRect2D &render_area, VkQueueFlags queue_flags,
const std::vector<const syncval_state::ImageViewState *> &attachment_views,
const AccessContext *external_context);
bool ValidateDrawSubpassAttachment(const CommandExecutionContext &ex_context, const CMD_BUFFER_STATE &cmd_buffer,
vvl::Func command) const;
void RecordDrawSubpassAttachment(const CMD_BUFFER_STATE &cmd_buffer, ResourceUsageTag tag);
bool ValidateClearAttachment(const CommandExecutionContext &ex_context, const CMD_BUFFER_STATE &cmd_buffer, const Location &loc,
const VkClearAttachment &clear_attachment, const VkClearRect &rect, uint32_t rect_index) const;
void RecordClearAttachment(const CMD_BUFFER_STATE &cmd_buffer, ResourceUsageTag tag, const VkClearAttachment &clear_attachment,
const VkClearRect &rect);
bool ValidateNextSubpass(const CommandExecutionContext &ex_context, vvl::Func command) const;
bool ValidateEndRenderPass(const CommandExecutionContext &ex_context, vvl::Func command) const;
bool ValidateFinalSubpassLayoutTransitions(const CommandExecutionContext &ex_context, vvl::Func command) const;
void RecordLayoutTransitions(ResourceUsageTag tag);
void RecordLoadOperations(ResourceUsageTag tag);
void RecordBeginRenderPass(ResourceUsageTag tag, ResourceUsageTag load_tag);
void RecordNextSubpass(ResourceUsageTag store_tag, ResourceUsageTag barrier_tag, ResourceUsageTag load_tag);
void RecordEndRenderPass(AccessContext *external_context, ResourceUsageTag store_tag, ResourceUsageTag barrier_tag);
AccessContext &CurrentContext() { return subpass_contexts_[current_subpass_]; }
const AccessContext &CurrentContext() const { return subpass_contexts_[current_subpass_]; }
const std::vector<AccessContext> &GetContexts() const { return subpass_contexts_; }
uint32_t GetCurrentSubpass() const { return current_subpass_; }
const RENDER_PASS_STATE *GetRenderPassState() const { return rp_state_; }
AccessContext *CreateStoreResolveProxy() const;
private:
struct ClearAttachmentInfo {
uint32_t attachment_index;
VkImageAspectFlags aspects_to_clear;
VkImageSubresourceRange subresource_range;
};
std::optional<ClearAttachmentInfo> GetClearAttachmentInfo(const VkClearAttachment &clear_attachment,
const VkClearRect &rect) const;
private:
const RENDER_PASS_STATE *rp_state_;
const VkRect2D render_area_;
uint32_t current_subpass_;
std::vector<AccessContext> subpass_contexts_;
AttachmentViewGenVector attachment_views_;
};
// Command execution context is the base class for command buffer and queue contexts
// Preventing unintented leakage of subclass specific state, storing enough information
// for message logging.
// TODO: determine where to draw the design split for tag tracking (is there anything command to Queues and CB's)
class CommandExecutionContext {
public:
using AccessLog = std::vector<ResourceUsageRecord>;
using CommandBufferSet = vvl::unordered_set<std::shared_ptr<const CMD_BUFFER_STATE>>;
CommandExecutionContext() : sync_state_(nullptr) {}
CommandExecutionContext(const SyncValidator *sync_validator) : sync_state_(sync_validator) {}
virtual ~CommandExecutionContext() = default;
virtual AccessContext *GetCurrentAccessContext() = 0;
virtual SyncEventsContext *GetCurrentEventsContext() = 0;
virtual const AccessContext *GetCurrentAccessContext() const = 0;
virtual const SyncEventsContext *GetCurrentEventsContext() const = 0;
virtual QueueId GetQueueId() const = 0;
const SyncValidator &GetSyncState() const {
assert(sync_state_);
return *sync_state_;
}
ResourceUsageRange ImportRecordedAccessLog(const CommandBufferAccessContext &recorded_context);
std::string FormatHazard(const HazardResult &hazard) const;
virtual ResourceUsageTag GetTagLimit() const = 0;
virtual VulkanTypedHandle Handle() const = 0;
virtual std::string FormatUsage(ResourceUsageTag tag) const = 0;
virtual void InsertRecordedAccessLogEntries(const CommandBufferAccessContext &cb_context) = 0;
virtual void BeginRenderPassReplaySetup(ReplayState &replay, const SyncOpBeginRenderPass &begin_op) {
// Must override if use by derived type is valid
assert(false);
}
virtual void NextSubpassReplaySetup(ReplayState &replay) {
// Must override if use by derived type is valid
assert(false);
}
virtual void EndRenderPassReplayCleanup(ReplayState &replay) {
// Must override if use by derived type is valid
assert(false);
}
bool ValidForSyncOps() const;
protected:
const SyncValidator *sync_state_;
};
class CommandBufferAccessContext : public CommandExecutionContext {
public:
using SyncOpPointer = std::shared_ptr<SyncOpBase>;
struct SyncOpEntry {
ResourceUsageTag tag;
SyncOpPointer sync_op;
SyncOpEntry(ResourceUsageTag tag_, SyncOpPointer &&sync_op_) : tag(tag_), sync_op(std::move(sync_op_)) {}
SyncOpEntry() = default;
SyncOpEntry(const SyncOpEntry &other) = default;
};
CommandBufferAccessContext(const SyncValidator *sync_validator = nullptr)
: CommandExecutionContext(sync_validator),
cb_state_(),
access_log_(std::make_shared<AccessLog>()),
cbs_referenced_(std::make_shared<CommandBufferSet>()),
command_number_(0),
subcommand_number_(0),
reset_count_(0),
cb_access_context_(),
current_context_(&cb_access_context_),
events_context_(),
render_pass_contexts_(),
current_renderpass_context_(),
sync_ops_() {}
CommandBufferAccessContext(SyncValidator &sync_validator, CMD_BUFFER_STATE *cb_state)
: CommandBufferAccessContext(&sync_validator) {
cb_state_ = cb_state;
}
struct AsProxyContext {};
CommandBufferAccessContext(const CommandBufferAccessContext &real_context, AsProxyContext dummy);
// NOTE: because this class is encapsulated in syncval_state::CommandBuffer, it isn't safe
// to use shared_from_this from the constructor.
void SetSelfReference() { cbs_referenced_->insert(cb_state_->shared_from_this()); }
~CommandBufferAccessContext() override = default;
const CommandExecutionContext &GetExecutionContext() const { return *this; }
void Destroy() {
// the cb self reference must be cleared or the command buffer reference count will never go to 0
cbs_referenced_.reset();
cb_state_ = nullptr;
}
void Reset() {
access_log_ = std::make_shared<AccessLog>();
cbs_referenced_ = std::make_shared<CommandBufferSet>();
if (cb_state_) {
cbs_referenced_->insert(cb_state_->shared_from_this());
}
sync_ops_.clear();
command_number_ = 0;
subcommand_number_ = 0;
reset_count_++;
command_handles_.clear();
cb_access_context_.Reset();
render_pass_contexts_.clear();
current_context_ = &cb_access_context_;
current_renderpass_context_ = nullptr;
events_context_.Clear();
}
std::string FormatUsage(ResourceUsageTag tag) const override;
std::string FormatUsage(const ResourceFirstAccess &access) const; // Only command buffers have "first usage"
AccessContext *GetCurrentAccessContext() override { return current_context_; }
SyncEventsContext *GetCurrentEventsContext() override { return &events_context_; }
const AccessContext *GetCurrentAccessContext() const override { return current_context_; }
const SyncEventsContext *GetCurrentEventsContext() const override { return &events_context_; }
QueueId GetQueueId() const override;
RenderPassAccessContext *GetCurrentRenderPassContext() { return current_renderpass_context_; }
const RenderPassAccessContext *GetCurrentRenderPassContext() const { return current_renderpass_context_; }
ResourceUsageTag RecordBeginRenderPass(vvl::Func command, const RENDER_PASS_STATE &rp_state, const VkRect2D &render_area,
const std::vector<const syncval_state::ImageViewState *> &attachment_views);
bool ValidateDispatchDrawDescriptorSet(VkPipelineBindPoint pipelineBindPoint, const Location &loc) const;
void RecordDispatchDrawDescriptorSet(VkPipelineBindPoint pipelineBindPoint, ResourceUsageTag tag);
bool ValidateDrawVertex(const std::optional<uint32_t> &vertexCount, uint32_t firstVertex, const Location &loc) const;
void RecordDrawVertex(const std::optional<uint32_t> &vertexCount, uint32_t firstVertex, ResourceUsageTag tag);
bool ValidateDrawVertexIndex(const std::optional<uint32_t> &indexCount, uint32_t firstIndex, const Location &loc) const;
void RecordDrawVertexIndex(const std::optional<uint32_t> &indexCount, uint32_t firstIndex, ResourceUsageTag tag);
bool ValidateDrawSubpassAttachment(const Location &loc) const;
void RecordDrawSubpassAttachment(ResourceUsageTag tag);
ResourceUsageTag RecordNextSubpass(vvl::Func command);
ResourceUsageTag RecordEndRenderPass(vvl::Func command);
void RecordDestroyEvent(EVENT_STATE *event_state);
bool ValidateFirstUse(CommandExecutionContext &exec_context, const ErrorObject &error_obj, uint32_t index) const;
void RecordExecutedCommandBuffer(const CommandBufferAccessContext &recorded_context);
void ResolveExecutedCommandBuffer(const AccessContext &recorded_context, ResourceUsageTag offset);
VkQueueFlags GetQueueFlags() const { return cb_state_ ? cb_state_->GetQueueFlags() : 0; }
ResourceUsageTag NextSubcommandTag(vvl::Func command, ResourceUsageRecord::SubcommandType subcommand);
ResourceUsageTag NextSubcommandTag(vvl::Func command, NamedHandle &&handle, ResourceUsageRecord::SubcommandType subcommand);
ResourceUsageTag GetTagLimit() const override { return access_log_->size(); }
VulkanTypedHandle Handle() const override {
if (cb_state_) {
return cb_state_->Handle();
}
return VulkanTypedHandle(static_cast<VkCommandBuffer>(VK_NULL_HANDLE), kVulkanObjectTypeCommandBuffer);
}
ResourceUsageTag NextCommandTag(vvl::Func command, NamedHandle &&handle,
ResourceUsageRecord::SubcommandType subcommand = ResourceUsageRecord::SubcommandType::kNone);
ResourceUsageTag NextCommandTag(vvl::Func command,
ResourceUsageRecord::SubcommandType subcommand = ResourceUsageRecord::SubcommandType::kNone);
ResourceUsageTag NextIndexedCommandTag(vvl::Func command, uint32_t index);
// NamedHandle must be constructable from args
template <class... Args>
void AddHandle(ResourceUsageTag tag, Args &&...args) {
assert(tag < access_log_->size());
if (tag < access_log_->size()) {
(*access_log_)[tag].AddHandle(std::forward<Args>(args)...);
}
}
std::shared_ptr<const CMD_BUFFER_STATE> GetCBStateShared() const { return cb_state_->shared_from_this(); }
const CMD_BUFFER_STATE &GetCBState() const {
assert(cb_state_);
return *cb_state_;
}
template <class T, class... Args>
void RecordSyncOp(Args &&...args) {
// T must be as derived from SyncOpBase or the compiler will flag the next line as an error.
SyncOpPointer sync_op(std::make_shared<T>(std::forward<Args>(args)...));
RecordSyncOp(std::move(sync_op)); // Call the non-template version
}
std::shared_ptr<AccessLog> GetAccessLogShared() const { return access_log_; }
std::shared_ptr<CommandBufferSet> GetCBReferencesShared() const { return cbs_referenced_; }
void InsertRecordedAccessLogEntries(const CommandBufferAccessContext &cb_context) override;
const std::vector<SyncOpEntry> &GetSyncOps() const { return sync_ops_; };
private:
// As this is passing around a shared pointer to record, move to avoid needless atomics.
void RecordSyncOp(SyncOpPointer &&sync_op);
// Note: since every CommandBufferAccessContext is encapsulated in its CommandBuffer object,
// a reference count is not needed here.
CMD_BUFFER_STATE *cb_state_;
std::shared_ptr<AccessLog> access_log_;
std::shared_ptr<CommandBufferSet> cbs_referenced_;
uint32_t command_number_;
uint32_t subcommand_number_;
uint32_t reset_count_;
small_vector<NamedHandle, 1> command_handles_;
AccessContext cb_access_context_;
AccessContext *current_context_;
SyncEventsContext events_context_;
// Don't need the following for an active proxy cb context
std::vector<std::unique_ptr<RenderPassAccessContext>> render_pass_contexts_;
RenderPassAccessContext *current_renderpass_context_;
std::vector<SyncOpEntry> sync_ops_;
};
// Allow keep track of the exec contexts replay state
class ReplayState {
public:
struct RenderPassReplayState {
// A minimal subset of the functionality present in the RenderPassAccessContext. Since the accesses are recorded in the
// first_use information of the recorded access contexts, s.t. all we need to support is the barrier/resolve operations
RenderPassReplayState() { Reset(); }
AccessContext *Begin(VkQueueFlags queue_flags, const SyncOpBeginRenderPass &begin_op_,
const AccessContext &external_context);
AccessContext *Next();
void End(AccessContext &external_context);
const SyncOpBeginRenderPass *begin_op = nullptr;
const AccessContext *replay_context = nullptr;
uint32_t subpass = VK_SUBPASS_EXTERNAL;
std::vector<AccessContext> subpass_contexts;
void Reset() {
begin_op = nullptr;
replay_context = nullptr;
subpass = VK_SUBPASS_EXTERNAL;
subpass_contexts.clear();
}
operator bool() const { return begin_op != nullptr; }
};
bool ValidateFirstUse();
bool DetectFirstUseHazard(const ResourceUsageRange &first_use_range) const;
ReplayState(CommandExecutionContext &exec_context, const CommandBufferAccessContext &recorded_context,
const ErrorObject &error_object, uint32_t index);
CommandExecutionContext &GetExecutionContext() const { return exec_context_; }
ResourceUsageTag GetBaseTag() const { return base_tag_; }
void BeginRenderPassReplaySetup(const SyncOpBeginRenderPass &begin_op) {
exec_context_.BeginRenderPassReplaySetup(*this, begin_op);
}
void NextSubpassReplaySetup() { exec_context_.NextSubpassReplaySetup(*this); }
void EndRenderPassReplayCleanup() { exec_context_.EndRenderPassReplayCleanup(*this); }
AccessContext *ReplayStateRenderPassBegin(VkQueueFlags queue_flags, const SyncOpBeginRenderPass &begin_op,
const AccessContext &external_context) {
return rp_replay_.Begin(queue_flags, begin_op, external_context);
}
AccessContext *ReplayStateRenderPassNext() { return rp_replay_.Next(); }
void ReplayStateRenderPassEnd(AccessContext &external_context) { rp_replay_.End(external_context); }
protected:
const AccessContext *GetRecordedAccessContext() const;
CommandExecutionContext &exec_context_;
const CommandBufferAccessContext &recorded_context_;
const ErrorObject &error_obj_;
const uint32_t index_;
const ResourceUsageTag base_tag_;
RenderPassReplayState rp_replay_;
};
namespace syncval_state {
class CommandBuffer : public CMD_BUFFER_STATE {
public:
CommandBufferAccessContext access_context;
CommandBuffer(SyncValidator *dev, VkCommandBuffer cb, const VkCommandBufferAllocateInfo *pCreateInfo,
const COMMAND_POOL_STATE *pool);
~CommandBuffer() { Destroy(); }
void NotifyInvalidate(const BASE_NODE::NodeList &invalid_nodes, bool unlink) override;
void Destroy() override;
void Reset() override;
};
} // namespace syncval_state
VALSTATETRACK_DERIVED_STATE_OBJECT(VkCommandBuffer, syncval_state::CommandBuffer, CMD_BUFFER_STATE)
class QueueSyncState;
// Store references to ResourceUsageRecords with global tag range within a batch
class BatchAccessLog {
public:
struct BatchRecord {
BatchRecord() = default;
BatchRecord(const BatchRecord &other) = default;
BatchRecord(BatchRecord &&other) = default;
BatchRecord(const QueueSyncState &q, uint64_t submit, uint32_t batch)
: queue(&q), submit_index(submit), batch_index(batch), cb_index(0), bias(0) {}
BatchRecord &operator=(const BatchRecord &other) = default;
const QueueSyncState *queue;
uint64_t submit_index;
uint32_t batch_index;
uint32_t cb_index;
ResourceUsageTag bias;
};
struct AccessRecord {
const BatchRecord *batch;
const ResourceUsageRecord *record;
bool IsValid() const { return batch && record; }
};
struct CBSubmitLog {
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)
: batch_(batch), cbs_(cbs), log_(log) {}
CBSubmitLog(const BatchRecord &batch, const CommandBufferAccessContext &cb)
: CBSubmitLog(batch, cb.GetCBReferencesShared(), cb.GetAccessLogShared()) {}
size_t Size() const { return log_->size(); }
AccessRecord operator[](ResourceUsageTag tag) const;
private:
BatchRecord batch_;
std::shared_ptr<const CommandExecutionContext::CommandBufferSet> cbs_;
std::shared_ptr<const CommandExecutionContext::AccessLog> log_;
};
ResourceUsageTag Import(const BatchRecord &batch, const CommandBufferAccessContext &cb_access);
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 operator[](ResourceUsageTag tag) const;
BatchAccessLog() {}
private:
using CBSubmitLogRangeMap = sparse_container::range_map<ResourceUsageTag, CBSubmitLog>;
CBSubmitLogRangeMap log_map_;
};
struct PresentedImageRecord {
ResourceUsageTag tag; // the global tag at presentation
uint32_t image_index;
uint32_t present_index;
std::weak_ptr<const syncval_state::Swapchain> swapchain_state;
std::shared_ptr<const syncval_state::ImageState> image;
};
struct PresentedImage : public PresentedImageRecord {
std::shared_ptr<QueueBatchContext> batch;
subresource_adapter::ImageRangeGenerator range_gen;
PresentedImage() = default;
void UpdateMemoryAccess(SyncStageAccessIndex usage, ResourceUsageTag tag, AccessContext &access_context) const;
PresentedImage(const SyncValidator &sync_state, const 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<const syncval_state::Swapchain> swapchain, uint32_t at_index);
bool Invalid() const { return BASE_NODE::Invalid(image); }
void ExportToSwapchain(SyncValidator &);
void SetImage(uint32_t at_index);
};
using PresentedImages = std::vector<PresentedImage>;
namespace syncval_state {
class Swapchain : public SWAPCHAIN_NODE {
public:
Swapchain(ValidationStateTracker *dev_data, const VkSwapchainCreateInfoKHR *pCreateInfo, VkSwapchainKHR swapchain);
~Swapchain() { Destroy(); }
void RecordPresentedImage(PresentedImage &&presented_images);
PresentedImage MovePresentedImage(uint32_t image_index);
std::shared_ptr<const Swapchain> shared_from_this() const { return SharedFromThisImpl(this); }
std::shared_ptr<Swapchain> shared_from_this() { return SharedFromThisImpl(this); }
private:
PresentedImages presented; // Build this on demand
};
} // namespace syncval_state
VALSTATETRACK_DERIVED_STATE_OBJECT(VkSwapchainKHR, syncval_state::Swapchain, SWAPCHAIN_NODE)
class QueueBatchContext : public CommandExecutionContext {
public:
class PresentResourceRecord : public AlternateResourceUsage::RecordBase {
public:
using Base_ = AlternateResourceUsage::RecordBase;
Base_::Record MakeRecord() const override;
~PresentResourceRecord() override {}
PresentResourceRecord(const PresentedImageRecord &presented) : presented_(presented) {}
std::ostream &Format(std::ostream &out, const SyncValidator &sync_state) const override;
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) {}
std::ostream &Format(std::ostream &out, const SyncValidator &sync_state) const override;
private:
PresentedImageRecord presented_;
ResourceUsageTag acquire_tag_;
vvl::Func command_;
};
using ConstBatchSet = vvl::unordered_set<std::shared_ptr<const QueueBatchContext>>;
using BatchSet = vvl::unordered_set<std::shared_ptr<QueueBatchContext>>;
static constexpr bool TruePred(const std::shared_ptr<const QueueBatchContext> &) { return true; }
struct CmdBufferEntry {
uint32_t index = 0;
std::shared_ptr<const syncval_state::CommandBuffer> cb;
CmdBufferEntry(uint32_t index_, std::shared_ptr<const syncval_state::CommandBuffer> &&cb_)
: index(index_), cb(std::move(cb_)) {}
};
using CommandBuffers = std::vector<CmdBufferEntry>;
QueueBatchContext(const SyncValidator &sync_state, const QueueSyncState &queue_state, uint64_t submit_index,
uint32_t batch_index);
QueueBatchContext(const SyncValidator &sync_state);
QueueBatchContext() = delete;
void Trim();
std::string FormatUsage(ResourceUsageTag tag) 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_; }
VkQueueFlags GetQueueFlags() const;
QueueId GetQueueId() const override;
void SetupBatchTags(const ResourceUsageRange &tag_range);
void SetupBatchTags();
void ResetEventsContext() { events_context_.Clear(); }
ResourceUsageTag GetTagLimit() const override { return batch_.bias; }
// begin is the tag bias / .size() is the number of total records that should eventually be in access_log_
ResourceUsageRange GetTagRange() const { return tag_range_; }
void InsertRecordedAccessLogEntries(const CommandBufferAccessContext &cb_context) override;
void SetTagBias(ResourceUsageTag);
// For Submit
void SetupAccessContext(const std::shared_ptr<const QueueBatchContext> &prev, const VkSubmitInfo2 &submit_info,
SignaledSemaphores &signaled_semaphores);
void SetupCommandBufferInfo(const VkSubmitInfo2 &submit_info);
bool DoQueueSubmitValidate(const SyncValidator &sync_state, QueueSubmitCmdState &cmd_state, const VkSubmitInfo2 &submit_info);
void ResolveSubmittedCommandBuffer(const AccessContext &recorded_context, ResourceUsageTag offset);
// For Present
void SetupAccessContext(const std::shared_ptr<const QueueBatchContext> &prev, const VkPresentInfoKHR &present_info,
const PresentedImages &presented_images, SignaledSemaphores &signaled);
bool DoQueuePresentValidate(const Location &loc, const PresentedImages &presented_images);
void DoPresentOperations(const PresentedImages &presented_images);
void LogPresentOperations(const PresentedImages &presented_images);
// 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 BeginRenderPassReplaySetup(ReplayState &replay, const SyncOpBeginRenderPass &begin_op) override;
void NextSubpassReplaySetup(ReplayState &replay) override;
void EndRenderPassReplayCleanup(ReplayState &replay) override;
void Cleanup();
private:
void CommonSetupAccessContext(const std::shared_ptr<const QueueBatchContext> &prev,
QueueBatchContext::ConstBatchSet &batches_resolved);
std::shared_ptr<QueueBatchContext> ResolveOneWaitSemaphore(VkSemaphore sem, const PresentedImages &presented_images,
SignaledSemaphores &signaled);
std::shared_ptr<QueueBatchContext> ResolveOneWaitSemaphore(VkSemaphore sem, VkPipelineStageFlags2 wait_mask,
SignaledSemaphores &signaled);
void ImportSyncTags(const QueueBatchContext &from);
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_;
// Clear these after validation and import, not valid after.
BatchAccessLog::BatchRecord batch_; // Holds the cumulative tag bias, and command buffer counts for Import support.
CommandBuffers command_buffers_;
ConstBatchSet async_batches_;
};
class QueueSyncState {
public:
constexpr static QueueId kQueueIdBase = QueueId(0);
constexpr static QueueId kQueueIdInvalid = ~kQueueIdBase;
constexpr static QueueId kQueueAny = kQueueIdInvalid - 1;
QueueSyncState(const std::shared_ptr<QUEUE_STATE> &queue_state, VkQueueFlags queue_flags, QueueId id)
: submit_index_(0), queue_state_(queue_state), last_batch_(), queue_flags_(queue_flags), id_(id) {}
VulkanTypedHandle Handle() const {
if (queue_state_) {
return queue_state_->Handle();
}
return VulkanTypedHandle(static_cast<VkQueue>(VK_NULL_HANDLE), kVulkanObjectTypeQueue);
}
std::shared_ptr<const QueueBatchContext> LastBatch() const { return last_batch_; }
std::shared_ptr<QueueBatchContext> LastBatch() { return last_batch_; }
void UpdateLastBatch(std::shared_ptr<QueueBatchContext> &&last);
const QUEUE_STATE *GetQueueState() const { return queue_state_.get(); }
VkQueueFlags GetQueueFlags() const { return queue_flags_; }
QueueId GetQueueId() const { return id_; }
uint64_t ReserveSubmitId() const; // Method is const but updates mutable sumbit_index atomically.
private:
mutable std::atomic<uint64_t> submit_index_;
std::shared_ptr<QUEUE_STATE> queue_state_;
std::shared_ptr<QueueBatchContext> last_batch_;
const VkQueueFlags queue_flags_;
QueueId id_;
};
// The converter needs to be more complex than simply an array of VkSubmitInfo2 structures.
// In order to convert from Info->Info2, arrays of VkSemaphoreSubmitInfo and VkCommandBufferSubmitInfo
// structures must be created for the pWaitSemaphoreInfos, pCommandBufferInfos, and pSignalSemaphoreInfos
// which comprise the converted VkSubmitInfo information. The created VkSubmitInfo2 structure then references the storage
// of the arrays, which must have a lifespan longer than the conversion, s.t. the ensuing valdation/record operations
// can reference them. The resulting VkSubmitInfo2 is then copied into an additional which takes the place of the pSubmits
// parameter.
struct SubmitInfoConverter {
struct BatchStore {
BatchStore(const VkSubmitInfo &info, VkQueueFlags queue_flags);
static VkSemaphoreSubmitInfo WaitSemaphore(const VkSubmitInfo &info, uint32_t index);
static VkCommandBufferSubmitInfo CommandBuffer(const VkSubmitInfo &info, uint32_t index);
static VkSemaphoreSubmitInfo SignalSemaphore(const VkSubmitInfo &info, uint32_t index, VkQueueFlags queue_flags);
std::vector<VkSemaphoreSubmitInfo> waits;
std::vector<VkCommandBufferSubmitInfo> cbs;
std::vector<VkSemaphoreSubmitInfo> signals;
VkSubmitInfo2 info2;
};
SubmitInfoConverter(uint32_t count, const VkSubmitInfo *infos, VkQueueFlags queue_flags);
std::vector<BatchStore> info_store;
std::vector<VkSubmitInfo2> info2s;
};
class SyncValidator : public ValidationStateTracker, public SyncStageAccess {
public:
using ImageState = syncval_state::ImageState;
using ImageViewState = syncval_state::ImageViewState;
using StateTracker = ValidationStateTracker;
using Func = vvl::Func;
using Struct = vvl::Struct;
using Field = vvl::Field;
SyncValidator() { container_type = LayerObjectTypeSyncValidation; }
// Global tag range for submitted command buffers resource usage logs
// Started the global tag count at 1 s.t. zero are invalid and ResourceUsageTag normalization can just zero them.
mutable std::atomic<ResourceUsageTag> tag_limit_{1}; // This is reserved in Validation phase, thus mutable and atomic
ResourceUsageRange ReserveGlobalTagRange(size_t tag_count) const; // Note that the tag_limit_ is mutable this has side effects
vvl::unordered_map<VkQueue, std::shared_ptr<QueueSyncState>> queue_sync_states_;
QueueId queue_id_limit_ = QueueSyncState::kQueueIdBase;
SignaledSemaphores signaled_semaphores_;
using SignaledFences = vvl::unordered_map<VkFence, FenceSyncState>;
using SignaledFence = SignaledFences::value_type;
SignaledFences waitable_fences_;
void ApplyTaggedWait(QueueId queue_id, ResourceUsageTag tag);
void ApplyAcquireWait(const AcquiredImage &acquired);
template <typename BatchOp>
void ForAllQueueBatchContexts(BatchOp &&op);
void UpdateFenceWaitInfo(VkFence fence, QueueId queue_id, ResourceUsageTag tag);
void UpdateFenceWaitInfo(VkFence fence, const PresentedImage &image, ResourceUsageTag tag);
void UpdateFenceWaitInfo(std::shared_ptr<const FENCE_STATE> &fence, FenceSyncState &&wait_info);
void WaitForFence(VkFence fence);
void UpdateSyncImageMemoryBindState(uint32_t count, const VkBindImageMemoryInfo *infos);
const QueueSyncState *GetQueueSyncState(VkQueue queue) const;
QueueSyncState *GetQueueSyncState(VkQueue queue);
std::shared_ptr<const QueueSyncState> GetQueueSyncStateShared(VkQueue queue) const;
std::shared_ptr<QueueSyncState> GetQueueSyncStateShared(VkQueue queue);
QueueId GetQueueIdLimit() const { return queue_id_limit_; }
QueueBatchContext::BatchSet GetQueueBatchSnapshot();
template <typename Predicate>
QueueBatchContext::ConstBatchSet GetQueueLastBatchSnapshot(Predicate &&pred) const;
QueueBatchContext::ConstBatchSet GetQueueLastBatchSnapshot() const {
return GetQueueLastBatchSnapshot(QueueBatchContext::TruePred);
};
template <typename Predicate>
QueueBatchContext::BatchSet GetQueueLastBatchSnapshot(Predicate &&pred);
QueueBatchContext::BatchSet GetQueueLastBatchSnapshot() { return GetQueueLastBatchSnapshot(QueueBatchContext::TruePred); };
std::shared_ptr<CMD_BUFFER_STATE> CreateCmdBufferState(VkCommandBuffer cb, const VkCommandBufferAllocateInfo *pCreateInfo,
const COMMAND_POOL_STATE *cmd_pool) override;
std::shared_ptr<SWAPCHAIN_NODE> CreateSwapchainState(const VkSwapchainCreateInfoKHR *create_info,
VkSwapchainKHR swapchain) final;
std::shared_ptr<IMAGE_STATE> CreateImageState(VkImage img, const VkImageCreateInfo *pCreateInfo,
VkFormatFeatureFlags2KHR features) final;
std::shared_ptr<IMAGE_STATE> CreateImageState(VkImage img, const VkImageCreateInfo *pCreateInfo, VkSwapchainKHR swapchain,
uint32_t swapchain_index, VkFormatFeatureFlags2KHR features) final;
std::shared_ptr<IMAGE_VIEW_STATE> CreateImageViewState(const std::shared_ptr<IMAGE_STATE> &image_state, VkImageView iv,
const VkImageViewCreateInfo *ci, VkFormatFeatureFlags2KHR ff,
const VkFilterCubicImageViewImageFormatPropertiesEXT &cubic_props) final;
void RecordCmdBeginRenderPass(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo *pRenderPassBegin,
const VkSubpassBeginInfo *pSubpassBeginInfo, Func command);
void RecordCmdNextSubpass(VkCommandBuffer commandBuffer, const VkSubpassBeginInfo *pSubpassBeginInfo,
const VkSubpassEndInfo *pSubpassEndInfo, Func command);
void RecordCmdEndRenderPass(VkCommandBuffer commandBuffer, const VkSubpassEndInfo *pSubpassEndInfo, Func command);
bool SupressedBoundDescriptorWAW(const HazardResult &hazard) const;
void CreateDevice(const VkDeviceCreateInfo *pCreateInfo) override;
bool ValidateBeginRenderPass(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo *pRenderPassBegin,
const VkSubpassBeginInfo *pSubpassBeginInfo, const ErrorObject &error_obj) const;
bool PreCallValidateCmdBeginRenderPass(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo *pRenderPassBegin,
VkSubpassContents contents, const ErrorObject &error_obj) const override;
bool PreCallValidateCmdBeginRenderPass2KHR(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo *pRenderPassBegin,
const VkSubpassBeginInfo *pSubpassBeginInfo,
const ErrorObject &error_obj) const override;
bool PreCallValidateCmdBeginRenderPass2(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo *pRenderPassBegin,
const VkSubpassBeginInfo *pSubpassBeginInfo,
const ErrorObject &error_obj) const override;
bool PreCallValidateCmdCopyBuffer(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkBuffer dstBuffer, uint32_t regionCount,
const VkBufferCopy *pRegions, const ErrorObject &error_obj) const override;
void PreCallRecordCmdCopyBuffer(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkBuffer dstBuffer, uint32_t regionCount,
const VkBufferCopy *pRegions) override;
bool PreCallValidateCmdCopyBuffer2KHR(VkCommandBuffer commandBuffer, const VkCopyBufferInfo2KHR *pCopyBufferInfo,
const ErrorObject &error_obj) const override;
bool PreCallValidateCmdCopyBuffer2(VkCommandBuffer commandBuffer, const VkCopyBufferInfo2 *pCopyBufferInfo,
const ErrorObject &error_obj) const override;
void RecordCmdCopyBuffer2(VkCommandBuffer commandBuffer, const VkCopyBufferInfo2KHR *pCopyBufferInfo, Func command);
void PreCallRecordCmdCopyBuffer2KHR(VkCommandBuffer commandBuffer, const VkCopyBufferInfo2KHR *pCopyBufferInfo) override;
void PreCallRecordCmdCopyBuffer2(VkCommandBuffer commandBuffer, const VkCopyBufferInfo2 *pCopyBufferInfo) override;
bool PreCallValidateCmdCopyImage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout,
VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount,
const VkImageCopy *pRegions, const ErrorObject &error_obj) const override;
void PreCallRecordCmdCopyImage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage,
VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageCopy *pRegions) override;
bool PreCallValidateCmdCopyImage2KHR(VkCommandBuffer commandBuffer, const VkCopyImageInfo2KHR *pCopyImageInfo,
const ErrorObject &error_obj) const override;
bool PreCallValidateCmdCopyImage2(VkCommandBuffer commandBuffer, const VkCopyImageInfo2 *pCopyImageInfo,
const ErrorObject &error_obj) const override;
void RecordCmdCopyImage2(VkCommandBuffer commandBuffer, const VkCopyImageInfo2 *pCopyImageInfo, Func command);
void PreCallRecordCmdCopyImage2KHR(VkCommandBuffer commandBuffer, const VkCopyImageInfo2KHR *pCopyImageInfo) override;
void PreCallRecordCmdCopyImage2(VkCommandBuffer commandBuffer, const VkCopyImageInfo2 *pCopyImageInfo) override;
bool PreCallValidateCmdPipelineBarrier(VkCommandBuffer commandBuffer, VkPipelineStageFlags srcStageMask,
VkPipelineStageFlags dstStageMask, VkDependencyFlags dependencyFlags,
uint32_t memoryBarrierCount, const VkMemoryBarrier *pMemoryBarriers,
uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier *pBufferMemoryBarriers,
uint32_t imageMemoryBarrierCount, const VkImageMemoryBarrier *pImageMemoryBarriers,
const ErrorObject &error_obj) const override;
void PreCallRecordCmdPipelineBarrier(VkCommandBuffer commandBuffer, VkPipelineStageFlags srcStageMask,
VkPipelineStageFlags dstStageMask, VkDependencyFlags dependencyFlags,
uint32_t memoryBarrierCount, const VkMemoryBarrier *pMemoryBarriers,
uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier *pBufferMemoryBarriers,
uint32_t imageMemoryBarrierCount,
const VkImageMemoryBarrier *pImageMemoryBarriers) override;
bool PreCallValidateCmdPipelineBarrier2KHR(VkCommandBuffer commandBuffer, const VkDependencyInfoKHR *pDependencyInfo,
const ErrorObject &error_obj) const override;
bool PreCallValidateCmdPipelineBarrier2(VkCommandBuffer commandBuffer, const VkDependencyInfo *pDependencyInfo,
const ErrorObject &error_obj) const override;
void PreCallRecordCmdPipelineBarrier2KHR(VkCommandBuffer commandBuffer, const VkDependencyInfoKHR *pDependencyInfo) override;
void PreCallRecordCmdPipelineBarrier2(VkCommandBuffer commandBuffer, const VkDependencyInfo *pDependencyInfo) override;
void PostCallRecordBeginCommandBuffer(VkCommandBuffer commandBuffer, const VkCommandBufferBeginInfo *pBeginInfo,
const RecordObject &record_obj) override;
void PostCallRecordCmdBeginRenderPass(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo *pRenderPassBegin,
VkSubpassContents contents, const RecordObject &record_obj) override;
void PostCallRecordCmdBeginRenderPass2(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo *pRenderPassBegin,
const VkSubpassBeginInfo *pSubpassBeginInfo, const RecordObject &record_obj) override;
void PostCallRecordCmdBeginRenderPass2KHR(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo *pRenderPassBegin,
const VkSubpassBeginInfo *pSubpassBeginInfo, const RecordObject &record_obj) override;
bool ValidateCmdNextSubpass(VkCommandBuffer commandBuffer, const VkSubpassBeginInfo *pSubpassBeginInfo,
const VkSubpassEndInfo *pSubpassEndInfo, const ErrorObject &error_obj) const;
bool PreCallValidateCmdNextSubpass(VkCommandBuffer commandBuffer, VkSubpassContents contents,
const ErrorObject &error_obj) const override;
bool PreCallValidateCmdNextSubpass2(VkCommandBuffer commandBuffer, const VkSubpassBeginInfo *pSubpassBeginInfo,
const VkSubpassEndInfo *pSubpassEndInfo, const ErrorObject &error_obj) const override;
bool PreCallValidateCmdNextSubpass2KHR(VkCommandBuffer commandBuffer, const VkSubpassBeginInfo *pSubpassBeginInfo,
const VkSubpassEndInfo *pSubpassEndInfo, const ErrorObject &error_obj) const override;
void PostCallRecordCmdNextSubpass(VkCommandBuffer commandBuffer, VkSubpassContents contents,
const RecordObject &record_obj) override;
void PostCallRecordCmdNextSubpass2(VkCommandBuffer commandBuffer, const VkSubpassBeginInfo *pSubpassBeginInfo,
const VkSubpassEndInfo *pSubpassEndInfo, const RecordObject &record_obj) override;
void PostCallRecordCmdNextSubpass2KHR(VkCommandBuffer commandBuffer, const VkSubpassBeginInfo *pSubpassBeginInfo,
const VkSubpassEndInfo *pSubpassEndInfo, const RecordObject &record_obj) override;
bool ValidateCmdEndRenderPass(VkCommandBuffer commandBuffer, const VkSubpassEndInfo *pSubpassEndInfo,
const ErrorObject &error_obj) const;
bool PreCallValidateCmdEndRenderPass(VkCommandBuffer commandBuffer, const ErrorObject &error_obj) const override;
bool PreCallValidateCmdEndRenderPass2KHR(VkCommandBuffer commandBuffer, const VkSubpassEndInfo *pSubpassEndInfo,
const ErrorObject &error_obj) const override;
bool PreCallValidateCmdEndRenderPass2(VkCommandBuffer commandBuffer, const VkSubpassEndInfo *pSubpassEndInfo,
const ErrorObject &error_obj) const override;
void PostCallRecordCmdEndRenderPass(VkCommandBuffer commandBuffer, const RecordObject &record_obj) override;
void PostCallRecordCmdEndRenderPass2(VkCommandBuffer commandBuffer, const VkSubpassEndInfo *pSubpassEndInfo,
const RecordObject &record_obj) override;
void PostCallRecordCmdEndRenderPass2KHR(VkCommandBuffer commandBuffer, const VkSubpassEndInfo *pSubpassEndInfo,
const RecordObject &record_obj) override;
template <typename RegionType>
bool ValidateCmdCopyBufferToImage(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkImage dstImage,
VkImageLayout dstImageLayout, uint32_t regionCount, const RegionType *pRegions,
const Location &loc) const;
bool PreCallValidateCmdCopyBufferToImage(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkImage dstImage,
VkImageLayout dstImageLayout, uint32_t regionCount, const VkBufferImageCopy *pRegions,
const ErrorObject &error_obj) const override;
bool PreCallValidateCmdCopyBufferToImage2KHR(VkCommandBuffer commandBuffer,
const VkCopyBufferToImageInfo2KHR *pCopyBufferToImageInfo,
const ErrorObject &error_obj) const override;
bool PreCallValidateCmdCopyBufferToImage2(VkCommandBuffer commandBuffer, const VkCopyBufferToImageInfo2 *pCopyBufferToImageInfo,
const ErrorObject &error_obj) const override;
template <typename RegionType>
void RecordCmdCopyBufferToImage(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkImage dstImage,
VkImageLayout dstImageLayout, uint32_t regionCount, const RegionType *pRegions, Func command);
void PreCallRecordCmdCopyBufferToImage(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkImage dstImage,
VkImageLayout dstImageLayout, uint32_t regionCount,
const VkBufferImageCopy *pRegions) override;
void PreCallRecordCmdCopyBufferToImage2KHR(VkCommandBuffer commandBuffer,
const VkCopyBufferToImageInfo2KHR *pCopyBufferToImageInfo) override;
void PreCallRecordCmdCopyBufferToImage2(VkCommandBuffer commandBuffer,
const VkCopyBufferToImageInfo2 *pCopyBufferToImageInfo) override;
template <typename RegionType>
bool ValidateCmdCopyImageToBuffer(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout,
VkBuffer dstBuffer, uint32_t regionCount, const RegionType *pRegions,
const Location &loc) const;
bool PreCallValidateCmdCopyImageToBuffer(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout,
VkBuffer dstBuffer, uint32_t regionCount, const VkBufferImageCopy *pRegions,
const ErrorObject &error_obj) const override;
bool PreCallValidateCmdCopyImageToBuffer2KHR(VkCommandBuffer commandBuffer,
const VkCopyImageToBufferInfo2KHR *pCopyImageToBufferInfo,
const ErrorObject &error_obj) const override;
bool PreCallValidateCmdCopyImageToBuffer2(VkCommandBuffer commandBuffer, const VkCopyImageToBufferInfo2 *pCopyImageToBufferInfo,
const ErrorObject &error_obj) const override;
template <typename RegionType>
void RecordCmdCopyImageToBuffer(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout,
VkBuffer dstBuffer, uint32_t regionCount, const RegionType *pRegions, Func command);
void PreCallRecordCmdCopyImageToBuffer(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout,
VkBuffer dstBuffer, uint32_t regionCount, const VkBufferImageCopy *pRegions) override;
void PreCallRecordCmdCopyImageToBuffer2KHR(VkCommandBuffer commandBuffer,
const VkCopyImageToBufferInfo2KHR *pCopyImageToBufferInfo) override;
void PreCallRecordCmdCopyImageToBuffer2(VkCommandBuffer commandBuffer,
const VkCopyImageToBufferInfo2 *pCopyImageToBufferInfo) override;
template <typename RegionType>
bool ValidateCmdBlitImage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage,
VkImageLayout dstImageLayout, uint32_t regionCount, const RegionType *pRegions, VkFilter filter,
const Location &loc) const;
bool PreCallValidateCmdBlitImage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout,
VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount,
const VkImageBlit *pRegions, VkFilter filter, const ErrorObject &error_obj) const override;
bool PreCallValidateCmdBlitImage2KHR(VkCommandBuffer commandBuffer, const VkBlitImageInfo2KHR *pBlitImageInfo,
const ErrorObject &error_obj) const override;
bool PreCallValidateCmdBlitImage2(VkCommandBuffer commandBuffer, const VkBlitImageInfo2 *pBlitImageInfo,
const ErrorObject &error_obj) const override;
template <typename RegionType>
void RecordCmdBlitImage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage,
VkImageLayout dstImageLayout, uint32_t regionCount, const RegionType *pRegions, VkFilter filter,
Func command);
void PreCallRecordCmdBlitImage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage,
VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageBlit *pRegions,
VkFilter filter) override;
void PreCallRecordCmdBlitImage2KHR(VkCommandBuffer commandBuffer, const VkBlitImageInfo2KHR *pBlitImageInfo) override;
void PreCallRecordCmdBlitImage2(VkCommandBuffer commandBuffer, const VkBlitImageInfo2 *pBlitImageInfo) override;
bool ValidateIndirectBuffer(const CommandBufferAccessContext &cb_context, const AccessContext &context,
VkCommandBuffer commandBuffer, const VkDeviceSize struct_size, const VkBuffer buffer,
const VkDeviceSize offset, const uint32_t drawCount, const uint32_t stride,
const Location &loc) const;
void RecordIndirectBuffer(AccessContext &context, ResourceUsageTag tag, const VkDeviceSize struct_size, const VkBuffer buffer,
const VkDeviceSize offset, const uint32_t drawCount, uint32_t stride);
bool ValidateCountBuffer(const CommandBufferAccessContext &cb_context, const AccessContext &context,
VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, const Location &loc) const;
void RecordCountBuffer(AccessContext &context, ResourceUsageTag tag, VkBuffer buffer, VkDeviceSize offset);
bool PreCallValidateCmdDispatch(VkCommandBuffer commandBuffer, uint32_t x, uint32_t y, uint32_t z,
const ErrorObject &error_obj) const override;
void PreCallRecordCmdDispatch(VkCommandBuffer commandBuffer, uint32_t x, uint32_t y, uint32_t z) override;
bool PreCallValidateCmdDispatchIndirect(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset,
const ErrorObject &error_obj) const override;
void PreCallRecordCmdDispatchIndirect(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset) override;
bool PreCallValidateCmdDraw(VkCommandBuffer commandBuffer, uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex,
uint32_t firstInstance, const ErrorObject &error_obj) const override;
void PreCallRecordCmdDraw(VkCommandBuffer commandBuffer, uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex,
uint32_t firstInstance) override;
bool PreCallValidateCmdDrawIndexed(VkCommandBuffer commandBuffer, uint32_t indexCount, uint32_t instanceCount,
uint32_t firstIndex, int32_t vertexOffset, uint32_t firstInstance,
const ErrorObject &error_obj) const override;
void PreCallRecordCmdDrawIndexed(VkCommandBuffer commandBuffer, uint32_t indexCount, uint32_t instanceCount,
uint32_t firstIndex, int32_t vertexOffset, uint32_t firstInstance) override;
bool PreCallValidateCmdDrawIndirect(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount,
uint32_t stride, const ErrorObject &error_obj) const override;
void PreCallRecordCmdDrawIndirect(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount,
uint32_t stride) override;
bool PreCallValidateCmdDrawIndexedIndirect(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset,
uint32_t drawCount, uint32_t stride, const ErrorObject &error_obj) const override;
void PreCallRecordCmdDrawIndexedIndirect(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset,
uint32_t drawCount, uint32_t stride) override;
bool PreCallValidateCmdDrawIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset,
VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount,
uint32_t stride, const ErrorObject &error_obj) const override;
void RecordCmdDrawIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer,
VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride, Func command);
void PreCallRecordCmdDrawIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset,
VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount,
uint32_t stride) override;
bool PreCallValidateCmdDrawIndirectCountKHR(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset,
VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount,
uint32_t stride, const ErrorObject &error_obj) const override;
void PreCallRecordCmdDrawIndirectCountKHR(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset,
VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount,
uint32_t stride) override;
bool PreCallValidateCmdDrawIndirectCountAMD(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset,
VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount,
uint32_t stride, const ErrorObject &error_obj) const override;
void PreCallRecordCmdDrawIndirectCountAMD(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset,
VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount,
uint32_t stride) override;
bool PreCallValidateCmdDrawIndexedIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset,
VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount,
uint32_t stride, const ErrorObject &error_obj) const override;
void RecordCmdDrawIndexedIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset,
VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount,
uint32_t stride, Func command);
void PreCallRecordCmdDrawIndexedIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset,
VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount,
uint32_t stride) override;
bool PreCallValidateCmdDrawIndexedIndirectCountKHR(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset,
VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount,
uint32_t stride, const ErrorObject &error_obj) const override;
void PreCallRecordCmdDrawIndexedIndirectCountKHR(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset,
VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount,
uint32_t stride) override;
bool PreCallValidateCmdDrawIndexedIndirectCountAMD(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset,
VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount,
uint32_t stride, const ErrorObject &error_obj) const override;
void PreCallRecordCmdDrawIndexedIndirectCountAMD(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset,
VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount,
uint32_t stride) override;
bool PreCallValidateCmdClearColorImage(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout,
const VkClearColorValue *pColor, uint32_t rangeCount,
const VkImageSubresourceRange *pRanges, const ErrorObject &error_obj) const override;
void PreCallRecordCmdClearColorImage(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout,
const VkClearColorValue *pColor, uint32_t rangeCount,
const VkImageSubresourceRange *pRanges) override;
bool PreCallValidateCmdClearDepthStencilImage(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout,
const VkClearDepthStencilValue *pDepthStencil, uint32_t rangeCount,
const VkImageSubresourceRange *pRanges,
const ErrorObject &error_obj) const override;
void PreCallRecordCmdClearDepthStencilImage(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout,
const VkClearDepthStencilValue *pDepthStencil, uint32_t rangeCount,
const VkImageSubresourceRange *pRanges) override;
bool PreCallValidateCmdClearAttachments(VkCommandBuffer commandBuffer, uint32_t attachmentCount,
const VkClearAttachment *pAttachments, uint32_t rectCount, const VkClearRect *pRects,
const ErrorObject &error_obj) const override;
void PreCallRecordCmdClearAttachments(VkCommandBuffer commandBuffer, uint32_t attachmentCount,
const VkClearAttachment *pAttachments, uint32_t rectCount,
const VkClearRect *pRects) override;
bool PreCallValidateCmdCopyQueryPoolResults(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t firstQuery,
uint32_t queryCount, VkBuffer dstBuffer, VkDeviceSize dstOffset,
VkDeviceSize stride, VkQueryResultFlags flags,
const ErrorObject &error_obj) const override;
void PreCallRecordCmdCopyQueryPoolResults(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t firstQuery,
uint32_t queryCount, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize stride,
VkQueryResultFlags flags) override;
bool PreCallValidateCmdFillBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize size,
uint32_t data, const ErrorObject &error_obj) const override;
void PreCallRecordCmdFillBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize size,
uint32_t data) override;
bool PreCallValidateCmdResolveImage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout,
VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount,
const VkImageResolve *pRegions, const ErrorObject &error_obj) const override;
void PreCallRecordCmdResolveImage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout,
VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount,
const VkImageResolve *pRegions) override;
bool PreCallValidateCmdResolveImage2KHR(VkCommandBuffer commandBuffer, const VkResolveImageInfo2KHR *pResolveImageInfo,
const ErrorObject &error_obj) const override;
bool PreCallValidateCmdResolveImage2(VkCommandBuffer commandBuffer, const VkResolveImageInfo2 *pResolveImageInfo,
const ErrorObject &error_obj) const override;
void PreCallRecordCmdResolveImage2KHR(VkCommandBuffer commandBuffer, const VkResolveImageInfo2KHR *pResolveImageInfo) override;
void PreCallRecordCmdResolveImage2(VkCommandBuffer commandBuffer, const VkResolveImageInfo2 *pResolveImageInfo) override;
void RecordCmdResolveImage2(VkCommandBuffer commandBuffer, const VkResolveImageInfo2 *pResolveImageInfo, Func command);
bool PreCallValidateCmdUpdateBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset,
VkDeviceSize dataSize, const void *pData, const ErrorObject &error_obj) const override;
void PreCallRecordCmdUpdateBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset,
VkDeviceSize dataSize, const void *pData) override;
bool PreCallValidateCmdWriteBufferMarkerAMD(VkCommandBuffer commandBuffer, VkPipelineStageFlagBits pipelineStage,
VkBuffer dstBuffer, VkDeviceSize dstOffset, uint32_t marker,
const ErrorObject &error_obj) const override;
void PreCallRecordCmdWriteBufferMarkerAMD(VkCommandBuffer commandBuffer, VkPipelineStageFlagBits pipelineStage,
VkBuffer dstBuffer, VkDeviceSize dstOffset, uint32_t marker) override;
bool PreCallValidateCmdSetEvent(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags stageMask,
const ErrorObject &error_obj) const override;
void PostCallRecordCmdSetEvent(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags stageMask,
const RecordObject &record_obj) override;
bool PreCallValidateCmdSetEvent2KHR(VkCommandBuffer commandBuffer, VkEvent event, const VkDependencyInfoKHR *pDependencyInfo,
const ErrorObject &error_obj) const override;
bool PreCallValidateCmdSetEvent2(VkCommandBuffer commandBuffer, VkEvent event, const VkDependencyInfo *pDependencyInfo,
const ErrorObject &error_obj) const override;
void PostCallRecordCmdSetEvent2KHR(VkCommandBuffer commandBuffer, VkEvent event, const VkDependencyInfoKHR *pDependencyInfo,
const RecordObject &record_obj) override;
void PostCallRecordCmdSetEvent2(VkCommandBuffer commandBuffer, VkEvent event, const VkDependencyInfo *pDependencyInfo,
const RecordObject &record_obj) override;
bool PreCallValidateCmdResetEvent(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags stageMask,
const ErrorObject &error_obj) const override;
void PostCallRecordCmdResetEvent(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags stageMask,
const RecordObject &record_obj) override;
bool PreCallValidateCmdResetEvent2KHR(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags2KHR stageMask,
const ErrorObject &error_obj) const override;
bool PreCallValidateCmdResetEvent2(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags2 stageMask,
const ErrorObject &error_obj) const override;
void PostCallRecordCmdResetEvent2KHR(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags2KHR stageMask,
const RecordObject &record_obj) override;
void PostCallRecordCmdResetEvent2(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags2 stageMask,
const RecordObject &record_obj) override;
bool PreCallValidateCmdWaitEvents(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent *pEvents,
VkPipelineStageFlags sourceStageMask, VkPipelineStageFlags dstStageMask,
uint32_t memoryBarrierCount, const VkMemoryBarrier *pMemoryBarriers,
uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier *pBufferMemoryBarriers,
uint32_t imageMemoryBarrierCount, const VkImageMemoryBarrier *pImageMemoryBarriers,
const ErrorObject &error_obj) const override;
void PostCallRecordCmdWaitEvents(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent *pEvents,
VkPipelineStageFlags sourceStageMask, VkPipelineStageFlags dstStageMask,
uint32_t memoryBarrierCount, const VkMemoryBarrier *pMemoryBarriers,
uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier *pBufferMemoryBarriers,
uint32_t imageMemoryBarrierCount, const VkImageMemoryBarrier *pImageMemoryBarriers,
const RecordObject &record_obj) override;
bool PreCallValidateCmdWaitEvents2KHR(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent *pEvents,
const VkDependencyInfoKHR *pDependencyInfos, const ErrorObject &error_obj) const override;
void PostCallRecordCmdWaitEvents2KHR(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent *pEvents,
const VkDependencyInfoKHR *pDependencyInfos, const RecordObject &record_obj) override;
bool PreCallValidateCmdWaitEvents2(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent *pEvents,
const VkDependencyInfo *pDependencyInfos, const ErrorObject &error_obj) const override;
void PostCallRecordCmdWaitEvents2(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent *pEvents,
const VkDependencyInfo *pDependencyInfos, const RecordObject &record_obj) override;
bool PreCallValidateCmdWriteBufferMarker2AMD(VkCommandBuffer commandBuffer, VkPipelineStageFlags2KHR stage, VkBuffer dstBuffer,
VkDeviceSize dstOffset, uint32_t marker,
const ErrorObject &error_obj) const override;
void PreCallRecordCmdWriteBufferMarker2AMD(VkCommandBuffer commandBuffer, VkPipelineStageFlags2KHR stage, VkBuffer dstBuffer,
VkDeviceSize dstOffset, uint32_t marker) override;
bool PreCallValidateCmdExecuteCommands(VkCommandBuffer commandBuffer, uint32_t commandBufferCount,
const VkCommandBuffer *pCommandBuffers, const ErrorObject &error_obj) const override;
void PreCallRecordCmdExecuteCommands(VkCommandBuffer commandBuffer, uint32_t commandBufferCount,
const VkCommandBuffer *pCommandBuffers) override;
void PostCallRecordBindImageMemory(VkDevice device, VkImage image, VkDeviceMemory mem, VkDeviceSize memoryOffset,
const RecordObject &record_obj) override;
void PostCallRecordBindImageMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfo *pBindInfos,
const RecordObject &record_obj) override;
void PostCallRecordBindImageMemory2KHR(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfo *pBindInfos,
const RecordObject &record_obj) override;
void PostCallRecordQueueWaitIdle(VkQueue queue, const RecordObject &record_obj) override;
void PostCallRecordDeviceWaitIdle(VkDevice device, const RecordObject &record_obj) override;
bool PreCallValidateQueuePresentKHR(VkQueue queue, const VkPresentInfoKHR *pPresentInfo,
const ErrorObject &error_obj) const override;
ResourceUsageRange SetupPresentInfo(const VkPresentInfoKHR &present_info, std::shared_ptr<QueueBatchContext> &batch,
PresentedImages &presented_images) const;
void PostCallRecordQueuePresentKHR(VkQueue queue, const VkPresentInfoKHR *pPresentInfo,
const RecordObject &record_obj) override;
void PostCallRecordAcquireNextImageKHR(VkDevice device, VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore,
VkFence fence, uint32_t *pImageIndex, const RecordObject &record_obj) override;
void PostCallRecordAcquireNextImage2KHR(VkDevice device, const VkAcquireNextImageInfoKHR *pAcquireInfo, uint32_t *pImageIndex,
const RecordObject &record_obj) override;
void RecordAcquireNextImageState(VkDevice device, VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore,
VkFence fence, uint32_t *pImageIndex, const RecordObject &record_obj);
bool ValidateQueueSubmit(VkQueue queue, uint32_t submitCount, const VkSubmitInfo2 *pSubmits, VkFence fence,
const ErrorObject &error_obj) const;
bool PreCallValidateQueueSubmit(VkQueue queue, uint32_t submitCount, const VkSubmitInfo *pSubmits, VkFence fence,
const ErrorObject &error_obj) const override;
void RecordQueueSubmit(VkQueue queue, VkFence fence, const RecordObject &record_obj);
void PostCallRecordQueueSubmit(VkQueue queue, uint32_t submitCount, const VkSubmitInfo *pSubmits, VkFence fence,
const RecordObject &record_obj) override;
bool PreCallValidateQueueSubmit2KHR(VkQueue queue, uint32_t submitCount, const VkSubmitInfo2KHR *pSubmits, VkFence fence,
const ErrorObject &error_obj) const override;
void PostCallRecordQueueSubmit2KHR(VkQueue queue, uint32_t submitCount, const VkSubmitInfo2KHR *pSubmits, VkFence fence,
const RecordObject &record_obj) override;
bool PreCallValidateQueueSubmit2(VkQueue queue, uint32_t submitCount, const VkSubmitInfo2KHR *pSubmits, VkFence fence,
const ErrorObject &error_obj) const override;
void PostCallRecordQueueSubmit2(VkQueue queue, uint32_t submitCount, const VkSubmitInfo2KHR *pSubmits, VkFence fence,
const RecordObject &record_obj) override;
void PostCallRecordGetFenceStatus(VkDevice device, VkFence fence, const RecordObject &record_obj) override;
void PostCallRecordWaitForFences(VkDevice device, uint32_t fenceCount, const VkFence *pFences, VkBool32 waitAll,
uint64_t timeout, const RecordObject &record_obj) override;
void PostCallRecordGetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR swapchain, uint32_t *pSwapchainImageCount,
VkImage *pSwapchainImages, const RecordObject &record_obj) override;
};