| /* |
| * Copyright (c) 2019-2026 Valve Corporation |
| * Copyright (c) 2019-2026 LunarG, Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| #pragma once |
| |
| #include "sync/sync_command.h" |
| #include "sync/sync_dynamic_rendering.h" |
| #include "sync/sync_event.h" |
| #include "sync/sync_render_pass.h" |
| #include "sync/sync_replay.h" |
| #include "state_tracker/cmd_buffer_state.h" |
| |
| struct RecordObject; |
| |
| namespace syncval { |
| class SyncValidator; |
| class ErrorMessages; |
| struct AccessStats; |
| |
| class AlternateResourceUsage { |
| public: |
| struct RecordBase; |
| struct RecordBase { |
| using Record = std::unique_ptr<RecordBase>; |
| virtual Record MakeRecord() const = 0; |
| virtual vvl::Func GetCommand() const = 0; |
| virtual VkSwapchainKHR GetSwapchainHandle() const = 0; |
| virtual ~RecordBase() {} |
| }; |
| |
| vvl::Func GetCommand() const { return record_->GetCommand(); } |
| VkSwapchainKHR GetSwapchainHandle() const { return record_->GetSwapchainHandle(); } |
| 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_; |
| }; |
| |
| // Vulkan handle and associated information. |
| // Command buffer context stores array of handles that are referenced by the tagged commands. |
| // VulkanTypedHandle is stored in unpacked form to avoid structure padding gaps. |
| struct HandleRecord { |
| uint64_t handle = 0; |
| VulkanObjectType type = kVulkanObjectTypeUnknown; |
| uint32_t index = vvl::kNoIndex32; |
| |
| HandleRecord() = default; |
| explicit HandleRecord(const VulkanTypedHandle& typed_handle, uint32_t index = vvl::kNoIndex32) |
| : handle(typed_handle.handle), type(typed_handle.type), index(index) {} |
| bool IsIndexed() const { return index != vvl::kNoIndex32; } |
| |
| VulkanTypedHandle TypedHandle() const { |
| VulkanTypedHandle typed_handle; |
| typed_handle.handle = handle; |
| typed_handle.type = type; |
| return typed_handle; |
| } |
| }; |
| |
| enum class SubCommandType { kNone, kSubpassTransition, kLoadOp, kStoreOp, kResolveOp, kIndex }; |
| |
| // ResourceUsageRecord encodes information about the command that performed the access. |
| // It's important to limit the size of this structure. Separate record is stored per access command. |
| struct ResourceUsageRecord { |
| static constexpr auto kMaxIndex = std::numeric_limits<ResourceUsageTag>::max(); |
| |
| ResourceUsageRecord(vvl::Func command, uint32_t seq_num, SubCommandType sub_type, const vvl::CommandBuffer* cb_state, |
| uint32_t reset_count, uint32_t subpass = vvl::kNoIndex32) |
| : command(command), |
| seq_num(seq_num), |
| sub_command_type(sub_type), |
| subpass(subpass), |
| cb_state(cb_state), |
| reset_count(reset_count) {} |
| ResourceUsageRecord(const AlternateResourceUsage& other) : alt_usage(other) {} |
| |
| vvl::Func command = vvl::Func::Empty; |
| |
| // TODO: this value should be relplaced by correct index of the Vulkan API command. Then it will be more useful. |
| // Currently this indexes only the commands that initiate memory accesses (so are of interest to syncval). |
| uint32_t seq_num = 0; |
| |
| SubCommandType sub_command_type = SubCommandType::kNone; |
| |
| uint32_t subpass = vvl::kNoIndex32; |
| |
| // 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 vvl::CommandBuffer* cb_state = nullptr; |
| uint32_t reset_count = 0; |
| |
| uint32_t first_handle_index = vvl::kNoIndex32; |
| uint32_t handle_count = 0; |
| |
| uint32_t label_command_index = vvl::kNoIndex32; |
| AlternateResourceUsage alt_usage; |
| }; |
| |
| // ResourceUsageInfo is similar to ResourceUsageRecord but prioritizes accessibility over memory efficiency. |
| // This structure can be as large as needed. Instances are usually stored on the stack. |
| struct ResourceUsageInfo { |
| vvl::Func command = vvl::Func::Empty; |
| uint32_t command_seq = vvl::kNoIndex32; |
| SubCommandType sub_command_type = SubCommandType::kNone; |
| uint32_t subpass = vvl::kNoIndex32; |
| |
| VulkanTypedHandle resource_handle; |
| std::string debug_region_name; |
| |
| const vvl::CommandBuffer* cb = nullptr; |
| uint32_t command_buffer_reset_count = 0; |
| |
| const vvl::Queue* queue = nullptr; |
| uint64_t submit_index = 0; |
| uint32_t batch_index = 0; |
| ResourceUsageTag batch_base_tag = 0; |
| }; |
| |
| // Provides debug region name for the specified access log command. |
| // If empty name is returned it means the command is not inside debug region. |
| struct DebugNameProvider { |
| virtual std::string GetDebugRegionName(const ResourceUsageRecord& record) const = 0; |
| }; |
| |
| struct ResourceUsageInfoProvider { |
| virtual ResourceUsageInfo GetResourceUsageInfo(ResourceUsageTagEx tag_ex) const = 0; |
| }; |
| |
| using AccessLog = std::vector<ResourceUsageRecord>; |
| using CommandBufferSet = std::vector<std::shared_ptr<const vvl::CommandBuffer>>; |
| |
| struct SyncEnvironment { |
| SyncEnvironment(const SyncValidator& validator, VkQueueFlags queue_flags, QueueId queue_id, VulkanTypedHandle handle, |
| SyncEventsContext& events_context, const ResourceUsageInfoProvider& usage_info_provider); |
| |
| const SyncValidator& validator; |
| const VkQueueFlags queue_flags; |
| const QueueId queue_id; |
| const VulkanTypedHandle handle; |
| SyncEventsContext& events_context; |
| const ResourceUsageInfoProvider& usage_info_provider; |
| }; |
| |
| class CommandBufferContext final : public ResourceUsageInfoProvider, public DebugNameProvider { |
| public: |
| struct AsProxyContext {}; |
| |
| CommandBufferContext(SyncValidator& sync_validator, vvl::CommandBuffer* cb_state); |
| CommandBufferContext(const CommandBufferContext& real_context, AsProxyContext dummy); |
| ~CommandBufferContext(); |
| |
| // NOTE: because this class is encapsulated in syncval::CommandBuffer, it isn't safe |
| // to use shared_from_this from the constructor. |
| void SetSelfReference() { cbs_referenced_->push_back(cb_state_->shared_from_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(); |
| |
| const SyncValidator& GetSyncState() const { return sync_state_; } |
| |
| ResourceUsageInfo GetResourceUsageInfo(ResourceUsageTagEx tag_ex) const override; |
| |
| AccessContext& GetCurrentAccessContext() { return *current_context_; } |
| const AccessContext& GetCurrentAccessContext() const { return *current_context_; } |
| QueueId GetQueueId() const; |
| |
| SyncEnvironment& GetSyncEnvironment() { return environment_; } |
| const SyncEnvironment& GetSyncEnvironment() const { return environment_; } |
| |
| // The command buffer's own access context. Subpass contexts exist only inside a vkCmdBeginRenderPass |
| // instance, so use this instead of GetCurrentAccessContext() anywhere else. Dynamic rendering has no |
| // subpass contexts, so this applies there too. |
| AccessContext& GetCbAccessContext() { return cb_access_context_; } |
| const AccessContext& GetCbAccessContext() const { return cb_access_context_; } |
| |
| RenderPassAccessContext* GetCurrentRenderPassContext() { return current_renderpass_context_; } |
| const RenderPassAccessContext* GetCurrentRenderPassContext() const { return current_renderpass_context_; } |
| uint32_t GetCurrentRenderPassInstanceId() const { return current_render_pass_instance_id_; } |
| ResourceUsageTag RecordBeginRenderPass(vvl::Func command, const vvl::RenderPass& rp_state, const VkRect2D& render_area, |
| const std::vector<std::shared_ptr<const vvl::ImageView>>& attachment_views); |
| |
| const RenderingInstance& BeginRenderingInstance(const VkRenderingInfo& rendering_info); |
| void EndRenderingInstance(); |
| const RenderingInstance* GetRenderingInstance() const { return rendering_instance_ ? &*rendering_instance_ : nullptr; } |
| |
| bool ValidateDispatchDrawDescriptorSet(VkPipelineBindPoint pipelineBindPoint, const Location& loc) const; |
| void RecordDispatchDrawDescriptorSet(VkPipelineBindPoint pipelineBindPoint, ResourceUsageTag tag); |
| |
| DescriptorAccesses CollectDescriptorAccesses(VkPipelineBindPoint pipelineBindPoint) const; |
| void RecordShaderAccesses(ResourceUsageTag tag, DescriptorAccesses& descriptor_accesses); |
| |
| DrawAttachmentCommand GetDrawAttachmentCommand() const; |
| |
| bool ValidateDrawVertex(uint32_t vertexCount, uint32_t firstVertex, const Location& loc) const; |
| void RecordDrawVertex(uint32_t vertexCount, uint32_t firstVertex, ResourceUsageTag tag); |
| bool ValidateDrawVertexIndex(uint32_t indexCount, uint32_t firstIndex, const Location& loc) const; |
| void RecordDrawVertexIndex(uint32_t indexCount, uint32_t firstIndex, ResourceUsageTag tag); |
| bool ValidateDrawAttachment(const Location& loc) const; |
| void RecordDrawAttachment(ResourceUsageTag tag); |
| bool ValidateClearAttachment(const Location& loc, const VkClearAttachment& clear_attachment, uint32_t clear_rect_index, |
| const VkClearRect& clear_rect) const; |
| void RecordClearAttachment(ResourceUsageTag tag, const VkClearAttachment& clear_attachment, const VkClearRect& clear_rect); |
| |
| ResourceUsageTag RecordNextSubpass(vvl::Func command); |
| ResourceUsageTag RecordEndRenderPass(vvl::Func command); |
| void RecordDestroyEvent(vvl::Event* event_state); |
| |
| void RecordExecutedCommandBuffer(const CommandBufferContext& recorded_context); |
| void ResolveExecutedCommandBuffer(const AccessContext& recorded_context, ResourceUsageTag offset); |
| |
| size_t GetTagCount() const { return access_log_->size(); } |
| |
| ResourceUsageTag NextCommandTag(vvl::Func command, SubCommandType subcommand = SubCommandType::kNone, |
| uint32_t subpass = vvl::kNoIndex32); |
| ResourceUsageTag NextSubCommandTag(vvl::Func command, SubCommandType subcommand, uint32_t subpass = vvl::kNoIndex32); |
| |
| ResourceUsageTagEx AddCommandHandle(ResourceUsageTag tag, const VulkanTypedHandle& typed_handle); |
| ResourceUsageTagEx AddCommandHandleIndexed(ResourceUsageTag tag, const VulkanTypedHandle& typed_handle, uint32_t index); |
| |
| // Default subcommand behavior is that it references the same handles as the main command. |
| // The following method allows to set subcommand handles independently of the main command. |
| void AddSubcommandHandleIndexed(ResourceUsageTag tag, const VulkanTypedHandle& typed_handle, uint32_t index); |
| |
| // NOTE: templated Operation is a GCC workaround (see the ReplayEntry constructor) |
| template <typename Operation> |
| void AddReplayEntry(ResourceUsageTag tag, bool validate_layout_transition_first_use, Operation&& operation) { |
| replay_entries_.emplace_back(tag, validate_layout_transition_first_use, std::forward<Operation>(operation)); |
| } |
| |
| template <typename Command> |
| void StoreCommand(ResourceUsageTag tag, const Command& command, uint32_t tag_count = 1) { |
| const auto storage = command.MakeStorage(command_data_); |
| commands_.push_back({command_data_.Store(storage), tag, tag_count}); |
| } |
| |
| const std::vector<HandleRecord>& GetHandleRecords() const { return handles_; } |
| |
| std::shared_ptr<const vvl::CommandBuffer> GetCBStateShared() const { return cb_state_->shared_from_this(); } |
| const vvl::CommandBuffer& GetCBState() const { return *cb_state_; } |
| |
| std::shared_ptr<AccessLog> GetAccessLogShared() const { return access_log_; } |
| std::shared_ptr<CommandBufferSet> GetCBReferencesShared() const { return cbs_referenced_; } |
| void ImportRecordedAccessLog(const CommandBufferContext& cb_context); |
| const std::vector<ReplayEntry>& GetReplayEntries() const { return replay_entries_; } |
| const std::vector<CommandEntry>& GetCommands() const { return commands_; } |
| const CommandData& GetCommandData() const { return command_data_; } |
| |
| // TODO: remove after convertion to commands is finished |
| bool HasAllCommands() const { |
| size_t command_index = 0; |
| for (ResourceUsageTag tag = 0; tag < access_log_->size(); tag++) { |
| if (command_index < commands_.size() && commands_[command_index].tag == tag) { |
| tag += commands_[command_index].tag_count - 1; |
| command_index++; |
| continue; |
| } |
| // We dont create replay commands for CmdExecuteCommands |
| const ResourceUsageRecord& record = (*access_log_)[tag]; |
| if (record.command == vvl::Func::vkCmdExecuteCommands && record.sub_command_type == SubCommandType::kIndex) { |
| continue; |
| } |
| return false; |
| } |
| return command_index == commands_.size(); |
| } |
| |
| // DebugNameProvider |
| std::string GetDebugRegionName(const ResourceUsageRecord& record) const override; |
| |
| std::vector<vvl::LabelCommand>& GetProxyLabelCommands() { return proxy_label_commands_; } |
| |
| void UpdateStats(AccessStats& access_stats) const; |
| |
| private: |
| CommandBufferContext(const SyncValidator& sync_validator, VkQueueFlags queue_flags, VulkanTypedHandle handle); |
| |
| uint32_t AddHandle(const VulkanTypedHandle& typed_handle, uint32_t index); |
| AttachmentAccess GetAttachmentAccess(SyncOrdering ordering, AttachmentAccessType type = AttachmentAccessType::Access) const; |
| |
| // Should be called only during render pass instance |
| uint32_t GetViewMask() const; |
| |
| struct ClearAttachmentInfo { |
| const vvl::ImageView& attachment_view; |
| VkImageSubresourceRange subresource_range{}; |
| }; |
| std::optional<ClearAttachmentInfo> GetClearAttachmentInfo(const VkClearAttachment& clear_attachment, uint32_t clear_first_layer, |
| uint32_t clear_layer_count) const; |
| VkImageAspectFlags GetAttachmentAspectsToClear(VkImageAspectFlags clear_aspect_mask, |
| const vvl::ImageView& attachment_view) const; |
| |
| void CheckCommandTagDebugCheckpoint(); |
| |
| private: |
| const SyncValidator& sync_state_; |
| const ErrorMessages& error_messages_; |
| |
| // Note: since every CommandBufferContext is encapsulated in its CommandBuffer object, |
| // a reference count is not needed here. |
| vvl::CommandBuffer* cb_state_ = nullptr; |
| |
| std::shared_ptr<AccessLog> access_log_; |
| std::shared_ptr<CommandBufferSet> cbs_referenced_; |
| uint32_t command_number_; |
| uint32_t reset_count_; |
| |
| // Handles referenced by the tagged commands |
| std::vector<HandleRecord> handles_; |
| |
| // Location of the current command in the access log (it's not always the last element, there might be |
| // subcommands that follow). The subcommands by default reference the same handles as the main command. |
| ResourceUsageTag current_command_tag_ = vvl::kNoIndex32; |
| |
| AccessContext cb_access_context_; |
| AccessContext* current_context_; |
| SyncEventsContext events_context_; |
| |
| SyncEnvironment environment_; |
| |
| // NOTE: Don't need the following for proxy contexts (that validate secondary execution). |
| // They flatten the source's current access state into cb_access_context_ |
| // |
| // TODO: After removing legacy submit-time replay, replace this context history and the |
| // non-owning current pointer with a single owning current context. |
| std::vector<std::unique_ptr<RenderPassAccessContext>> render_pass_contexts_; |
| RenderPassAccessContext* current_renderpass_context_; |
| std::vector<ReplayEntry> replay_entries_; |
| |
| std::vector<CommandEntry> commands_; |
| CommandData command_data_; |
| |
| // Dynamic rendering state |
| std::vector<RenderingAttachment> rendering_attachments_; |
| std::optional<RenderingInstance> rendering_instance_; |
| std::vector<ImageRangeGen> rendering_view_gens_; |
| |
| // Secondary buffer validation uses proxy context and does local update (imitates Record). |
| // Because in this case PreRecord is not called, the label state is not updated. We make |
| // a copy of label state to update it locally together with proxy context. |
| std::vector<vvl::LabelCommand> proxy_label_commands_; |
| |
| // Zero-based render pass instance id, incremented for each render pass instance. |
| // Used to initialize the corresponding value in the access object during recording. |
| // At submit time, these ids are offset to ensure unique values among all submitted |
| // command buffers. |
| uint32_t current_render_pass_instance_id_ = 0; |
| }; |
| |
| class CommandBufferSubState : public vvl::CommandBufferSubState { |
| public: |
| CommandBufferContext cb_context; |
| |
| CommandBufferSubState(SyncValidator& dev, vvl::CommandBuffer& cb); |
| |
| void NotifyInvalidate(const vvl::StateObject::NodeList& invalid_nodes, bool unlink) override; |
| |
| void End() override; |
| void Destroy() override; |
| void Reset(const Location& loc) override; |
| |
| void RecordCopyBuffer(vvl::Buffer& src_buffer_state, vvl::Buffer& dst_buffer_state, uint32_t region_count, |
| const VkBufferCopy* regions, const Location& loc) override; |
| void RecordCopyBuffer2(vvl::Buffer& src_buffer_state, vvl::Buffer& dst_buffer_state, uint32_t region_count, |
| const VkBufferCopy2* regions, const Location& loc) override; |
| void RecordCopyImage(vvl::Image& src_image_state, vvl::Image& dst_image_state, VkImageLayout src_image_layout, |
| VkImageLayout dst_image_layout, uint32_t region_count, const VkImageCopy* regions, |
| const Location& loc) override; |
| void RecordCopyImage2(vvl::Image& src_image_state, vvl::Image& dst_image_state, VkImageLayout src_image_layout, |
| VkImageLayout dst_image_layout, uint32_t region_count, const VkImageCopy2* regions, |
| const Location& loc) override; |
| void RecordCopyBufferToImage(vvl::Buffer& src_buffer_state, vvl::Image& dst_image_state, VkImageLayout dst_image_layout, |
| uint32_t region_count, const VkBufferImageCopy* regions, const Location& loc) override; |
| void RecordCopyBufferToImage2(vvl::Buffer& src_buffer_state, vvl::Image& dst_image_state, VkImageLayout dst_image_layout, |
| uint32_t region_count, const VkBufferImageCopy2* regions, const Location& loc) override; |
| void RecordCopyImageToBuffer(vvl::Image& src_image_state, vvl::Buffer& dst_buffer_state, VkImageLayout src_image_layout, |
| uint32_t region_count, const VkBufferImageCopy* regions, const Location& loc) override; |
| void RecordCopyImageToBuffer2(vvl::Image& src_image_state, vvl::Buffer& dst_buffer_state, VkImageLayout src_image_layout, |
| uint32_t region_count, const VkBufferImageCopy2* regions, const Location& loc) override; |
| void RecordBlitImage(vvl::Image& src_image_state, vvl::Image& dst_image_state, VkImageLayout src_image_layout, |
| VkImageLayout dst_image_layout, uint32_t region_count, const VkImageBlit* regions, |
| const Location& loc) override; |
| void RecordBlitImage2(vvl::Image& src_image_state, vvl::Image& dst_image_state, VkImageLayout src_image_layout, |
| VkImageLayout dst_image_layout, uint32_t region_count, const VkImageBlit2* regions, |
| const Location& loc) override; |
| void RecordResolveImage(vvl::Image& src_image_state, vvl::Image& dst_image_state, uint32_t region_count, |
| const VkImageResolve* regions, const Location& loc) override; |
| void RecordResolveImage2(vvl::Image& src_image_state, vvl::Image& dst_image_state, uint32_t region_count, |
| const VkImageResolve2* regions, const Location& loc) override; |
| void RecordClearColorImage(vvl::Image& image_state, VkImageLayout image_layout, const VkClearColorValue* color_values, |
| uint32_t range_count, const VkImageSubresourceRange* ranges, const Location& loc) override; |
| void RecordClearDepthStencilImage(vvl::Image& image_state, VkImageLayout image_layout, |
| const VkClearDepthStencilValue* depth_stencil_values, uint32_t range_count, |
| const VkImageSubresourceRange* ranges, const Location& loc) override; |
| void RecordClearAttachments(uint32_t attachment_count, const VkClearAttachment* pAttachments, uint32_t rect_count, |
| const VkClearRect* pRects, const Location& loc) override; |
| void RecordFillBuffer(vvl::Buffer& buffer_state, VkDeviceSize offset, VkDeviceSize size, const Location& loc) override; |
| void RecordUpdateBuffer(vvl::Buffer& buffer_state, VkDeviceSize offset, VkDeviceSize size, const Location& loc) override; |
| |
| void RecordDecodeVideo(vvl::VideoSession& vs_state, const VkVideoDecodeInfoKHR& decode_info, const Location& loc) override; |
| void RecordEncodeVideo(vvl::VideoSession& vs_state, const VkVideoEncodeInfoKHR& encode_info, const Location& loc) override; |
| |
| void RecordCopyQueryPoolResults(vvl::QueryPool& pool_state, vvl::Buffer& dst_buffer_state, uint32_t first_query, |
| uint32_t query_count, VkDeviceSize dst_offset, VkDeviceSize stride, VkQueryResultFlags flags, |
| const Location& loc) override; |
| |
| void RecordBeginRenderPass(const VkRenderPassBeginInfo& render_pass_begin, const VkSubpassBeginInfo& subpass_begin_info, |
| const Location& loc) override; |
| void RecordNextSubpass(const VkSubpassBeginInfo& subpass_begin_info, const VkSubpassEndInfo* subpass_end_info, |
| const Location& loc) override; |
| void RecordEndRenderPass(const VkSubpassEndInfo* subpass_end_info, const Location& loc) override; |
| |
| void RecordExecuteCommand(vvl::CommandBuffer& secondary_command_buffer, uint32_t cmd_index, const Location& loc) override; |
| }; |
| |
| static inline CommandBufferSubState& SubState(vvl::CommandBuffer& cb) { |
| return *static_cast<CommandBufferSubState*>(cb.SubState(LayerObjectTypeSyncValidation)); |
| } |
| static inline const CommandBufferSubState& SubState(const vvl::CommandBuffer& cb) { |
| return *static_cast<const CommandBufferSubState*>(cb.SubState(LayerObjectTypeSyncValidation)); |
| } |
| |
| static inline CommandBufferContext& GetCommandBufferContext(vvl::CommandBuffer& cb) { |
| return static_cast<CommandBufferSubState*>(cb.SubState(LayerObjectTypeSyncValidation))->cb_context; |
| } |
| static inline const CommandBufferContext& GetCommandBufferContext(const vvl::CommandBuffer& cb) { |
| return static_cast<const CommandBufferSubState*>(cb.SubState(LayerObjectTypeSyncValidation))->cb_context; |
| } |
| |
| } // namespace syncval |