| /* Copyright (c) 2019-2026 The Khronos Group Inc. |
| * 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. |
| */ |
| |
| #include "sync/sync_render_pass.h" |
| #include "sync/sync_validation.h" |
| #include "sync/sync_replay.h" |
| #include "sync/sync_image.h" |
| #include "state_tracker/render_pass_state.h" |
| #include "state_tracker/pipeline_state.h" |
| #include "utils/vk_api_utils.h" |
| #include "utils/math_utils.h" |
| #include <vulkan/utility/vk_format_utils.h> |
| |
| namespace syncval { |
| |
| class ValidateResolveAction { |
| public: |
| ValidateResolveAction(const SyncEnvironment& env, VulkanTypedHandle render_pass_handle, uint32_t subpass, uint32_t view_mask, |
| const AccessContext& access_context, const CommandBufferContext& cb_context, ResourceUsageTag replay_tag, |
| const Location& loc) |
| : env_(env), |
| render_pass_handle_(render_pass_handle), |
| subpass_(subpass), |
| view_mask_(view_mask), |
| context_(access_context), |
| cb_context_(cb_context), |
| replay_tag_(replay_tag), |
| loc_(loc), |
| skip_(false) {} |
| |
| void operator()(const char* aspect_name, const char* resolve_action_name, uint32_t src_at, uint32_t dst_at, |
| const AttachmentViewGen& view_gen, AttachmentViewGen::Gen gen_type, SyncAccessIndex current_usage, |
| const AttachmentAccess& attachment_access) { |
| const HazardResult hazard = |
| context_.DetectAttachmentHazard(view_gen, gen_type, current_usage, attachment_access, view_mask_, env_.queue_id); |
| if (hazard.IsHazard()) { |
| const SyncValidator& validator = env_.validator; |
| const LogObjectList objlist = BaseObjectList(env_, cb_context_, render_pass_handle_); |
| |
| std::ostringstream ss; |
| ss << validator.FormatHandle(view_gen.GetViewState()->Handle()); |
| ss << " (" << resolve_action_name << " of " << aspect_name << " multisample attachment " << src_at; |
| ss << " in subpass " << subpass_ << " of " << validator.FormatHandle(render_pass_handle_) << ")"; |
| const std::string resource_description = ss.str(); |
| const std::string error = validator.error_messages_.RenderPassResolveError(env_, hazard, cb_context_, replay_tag_, loc_, |
| resource_description); |
| skip_ |= validator.SyncError(hazard.Hazard(), objlist, loc_, error); |
| } |
| } |
| // Providing a mechanism for the constructing caller to get the result of the validation |
| bool GetSkip() const { return skip_; } |
| |
| private: |
| const SyncEnvironment& env_; |
| const VulkanTypedHandle render_pass_handle_; |
| const uint32_t subpass_; |
| const uint32_t view_mask_; |
| const AccessContext& context_; |
| const CommandBufferContext& cb_context_; |
| const ResourceUsageTag replay_tag_; |
| const Location& loc_; |
| bool skip_; |
| }; |
| |
| class UpdateStateResolveAction { |
| public: |
| UpdateStateResolveAction(AccessContext& context, uint32_t view_mask, ResourceUsageTag tag, QueueId queue_id) |
| : context_(context), view_mask_(view_mask), tag_(tag), queue_id_(queue_id) {} |
| void operator()(const char*, const char*, uint32_t, uint32_t, const AttachmentViewGen& view_gen, |
| AttachmentViewGen::Gen gen_type, SyncAccessIndex current_usage, const AttachmentAccess& attachment_access) { |
| context_.UpdateAttachmentAccessState(view_gen, gen_type, current_usage, attachment_access, ResourceUsageTagEx{tag_}, |
| view_mask_, queue_id_); |
| } |
| |
| private: |
| AccessContext& context_; |
| const uint32_t view_mask_; |
| const ResourceUsageTag tag_; |
| const QueueId queue_id_; |
| }; |
| |
| std::unique_ptr<AccessContext[]> InitSubpassContexts(VkQueueFlags queue_flags, const vvl::RenderPass& rp_state, |
| const AccessContext& external_context, QueueId queue_id) { |
| const uint32_t subpass_count = rp_state.create_info.subpassCount; |
| auto subpass_contexts = std::make_unique<AccessContext[]>(subpass_count); |
| // Add this for all subpasses here so that they exsist during next subpass validation |
| for (uint32_t pass = 0; pass < subpass_count; pass++) { |
| subpass_contexts[pass].validator = external_context.validator; |
| subpass_contexts[pass].InitFrom(pass, queue_flags, rp_state.subpass_dependency_infos, subpass_contexts.get(), |
| external_context, queue_id); |
| } |
| return subpass_contexts; |
| } |
| |
| static SyncAccessIndex ColorLoadUsage(VkAttachmentLoadOp load_op) { |
| if (load_op == VK_ATTACHMENT_LOAD_OP_NONE) { |
| return SYNC_ACCESS_INDEX_NONE; |
| } else if (load_op == VK_ATTACHMENT_LOAD_OP_LOAD) { |
| return SYNC_COLOR_ATTACHMENT_OUTPUT_COLOR_ATTACHMENT_READ; |
| } else { |
| return SYNC_COLOR_ATTACHMENT_OUTPUT_COLOR_ATTACHMENT_WRITE; |
| } |
| } |
| |
| static SyncAccessIndex DepthStencilLoadUsage(VkAttachmentLoadOp load_op) { |
| if (load_op == VK_ATTACHMENT_LOAD_OP_NONE) { |
| return SYNC_ACCESS_INDEX_NONE; |
| } else if (load_op == VK_ATTACHMENT_LOAD_OP_LOAD) { |
| return SYNC_EARLY_FRAGMENT_TESTS_DEPTH_STENCIL_ATTACHMENT_READ; |
| } else { |
| return SYNC_EARLY_FRAGMENT_TESTS_DEPTH_STENCIL_ATTACHMENT_WRITE; |
| } |
| } |
| |
| // Keeps only those bits in view_mask for which subpass is "enabled" based on subpass_per_view. |
| static std::optional<uint32_t> FilterViewMask(uint32_t view_mask, uint32_t subpass, |
| const vvl::RenderPass::SubpassPerView& subpass_per_view) { |
| // Special case when multiview is disabled to unify caller code |
| if (view_mask == 0) { |
| return (subpass_per_view[0] == subpass) ? std::optional<uint32_t>(0) : std::nullopt; |
| } |
| |
| uint32_t filtered_view_mask = 0; |
| const auto view_indices = GetSetBitIndices(view_mask); |
| for (uint8_t view_index : view_indices) { |
| if (subpass_per_view[view_index] == subpass) { |
| filtered_view_mask |= 1 << view_index; |
| } |
| } |
| return (filtered_view_mask != 0) ? std::optional<uint32_t>(filtered_view_mask) : std::nullopt; |
| } |
| |
| // Caller must manage returned pointer |
| static AccessContext* CreateStoreResolveProxyContext(const AccessContext& context, const vvl::RenderPass& rp_state, |
| uint32_t render_pass_instance_id, uint32_t subpass, uint32_t view_mask, |
| const AttachmentViewGenVector& attachment_views, QueueId queue_id) { |
| auto* proxy = new AccessContext(*context.validator); |
| proxy->InitFrom(context); |
| RenderPassAccessContext::UpdateAttachmentResolveAccess(rp_state, attachment_views, render_pass_instance_id, subpass, view_mask, |
| kInvalidTag, *proxy, queue_id); |
| RenderPassAccessContext::UpdateAttachmentStoreAccess(rp_state, attachment_views, render_pass_instance_id, subpass, view_mask, |
| kInvalidTag, *proxy, queue_id); |
| return proxy; |
| } |
| |
| // Layout transitions are handled as if the were occuring in the beginning of the next subpass |
| bool RenderPassAccessContext::ValidateLayoutTransitions(const SyncEnvironment& env, const AccessContext& access_context, |
| const vvl::RenderPass& rp_state, uint32_t render_pass_instance_id, |
| uint32_t subpass, uint32_t view_mask, |
| const AttachmentViewGenVector& attachment_views, |
| const CommandBufferContext& cb_context, ResourceUsageTag replay_tag, |
| const Location& loc) { |
| bool skip = false; |
| // As validation methods are const and precede the record/update phase, for any tranistions from the immediately |
| // previous subpass, we have to validate them against a copy of the AccessContext, with resolve operations applied, as |
| // those affects have not been recorded yet. |
| // |
| // Note: we could be more efficient by tracking whether or not we actually *have* any changes (e.g. attachment resolve) |
| // to apply and only copy then, if this proves a hot spot. |
| std::unique_ptr<AccessContext> src_context_proxy; |
| SubpassBarrier proxy_subpass_barrier; |
| |
| const auto& transitions = rp_state.subpass_transitions[subpass]; |
| for (const auto& transition : transitions) { |
| const SubpassBarrier& subpass_barrier = access_context.GetSubpassBarrier(transition.src_subpass); |
| const SubpassBarrier* p_subpass_barrier = &subpass_barrier; |
| |
| const bool src_context_needs_proxy = |
| transition.src_subpass != VK_SUBPASS_EXTERNAL && (transition.src_subpass + 1 == subpass); |
| |
| if (src_context_needs_proxy) { |
| if (!src_context_proxy) { |
| // TODO: this looks wrong to create proxy once for all iterations. |
| // Proxy depends on current iteration (transition.src_subpass), so it should be recreated |
| // each time when needed. Write a test that exposes this and make a fix. |
| src_context_proxy.reset(CreateStoreResolveProxyContext(*subpass_barrier.src_subpass_context, rp_state, |
| render_pass_instance_id, transition.src_subpass, view_mask, |
| attachment_views, env.queue_id)); |
| proxy_subpass_barrier = subpass_barrier; |
| proxy_subpass_barrier.src_subpass_context = src_context_proxy.get(); |
| } |
| p_subpass_barrier = &proxy_subpass_barrier; |
| } |
| auto hazard = access_context.DetectSubpassTransitionHazard(*p_subpass_barrier, attachment_views[transition.attachment]); |
| if (hazard.IsHazard()) { |
| const SyncValidator& validator = env.validator; |
| const LogObjectList objlist = BaseObjectList(env, cb_context, rp_state.Handle()); |
| |
| const vvl::ImageView* attachment_view = attachment_views[transition.attachment].GetViewState(); |
| std::ostringstream ss; |
| ss << "in subpass " << subpass << " of " << validator.FormatHandle(rp_state.Handle()); |
| ss << " on attachment " << transition.attachment << " ("; |
| ss << validator.FormatHandle(attachment_view->Handle()); |
| ss << ", " << validator.FormatHandle(attachment_view->image_state->Handle()); |
| ss << ", oldLayout " << string_VkImageLayout(transition.old_layout); |
| ss << ", newLayout " << string_VkImageLayout(transition.new_layout); |
| ss << ")"; |
| const std::string resource_description = ss.str(); |
| |
| if (hazard.Tag() == kInvalidTag) { |
| const std::string error = validator.error_messages_.RenderPassLayoutTransitionVsResolveError( |
| env, hazard, cb_context, replay_tag, loc, resource_description, rp_state.Handle(), transition.old_layout, |
| transition.new_layout, transition.src_subpass); |
| skip |= validator.SyncError(hazard.Hazard(), objlist, loc, error); |
| } else { |
| const std::string error = validator.error_messages_.RenderPassLayoutTransitionError( |
| env, hazard, cb_context, replay_tag, loc, resource_description, transition.old_layout, transition.new_layout); |
| skip |= validator.SyncError(hazard.Hazard(), objlist, loc, error); |
| } |
| } |
| } |
| return skip; |
| } |
| |
| bool RenderPassAccessContext::ValidateLoadOperation(const SyncEnvironment& env, const AccessContext& access_context, |
| const vvl::RenderPass& rp_state, uint32_t render_pass_instance_id, |
| uint32_t subpass, uint32_t view_mask, |
| const AttachmentViewGenVector& attachment_views, |
| const CommandBufferContext& cb_context, ResourceUsageTag replay_tag, |
| const Location& loc) { |
| bool skip = false; |
| |
| AttachmentAccess attachment_access; |
| attachment_access.type = AttachmentAccessType::LoadOp; |
| attachment_access.render_pass_instance_id = render_pass_instance_id; |
| attachment_access.subpass = subpass; |
| |
| for (uint32_t i = 0; i < rp_state.create_info.attachmentCount; i++) { |
| if (auto filtered_view_mask = FilterViewMask(view_mask, subpass, rp_state.attachment_first_subpass[i])) { |
| const auto& view_gen = attachment_views[i]; |
| const auto& ci = rp_state.create_info.pAttachments[i]; |
| |
| // Need check in the following way |
| // 1) if the usage bit isn't in the dest_access_scope, and there is layout traniition for initial use, report hazard |
| // vs. transition |
| // 2) if there isn't a layout transition, we need to look at the external context with a "detect hazard" operation |
| // for each aspect loaded. |
| |
| const bool has_depth = vkuFormatHasDepth(ci.format); |
| const bool has_stencil = vkuFormatHasStencil(ci.format); |
| const bool is_color = !(has_depth || has_stencil); |
| |
| const SyncAccessIndex load_index = has_depth ? DepthStencilLoadUsage(ci.loadOp) : ColorLoadUsage(ci.loadOp); |
| const SyncAccessIndex stencil_load_index = has_stencil ? DepthStencilLoadUsage(ci.stencilLoadOp) : load_index; |
| |
| HazardResult hazard; |
| const char* aspect = nullptr; |
| |
| bool checked_stencil = false; |
| if (is_color && (load_index != SYNC_ACCESS_INDEX_NONE)) { |
| attachment_access.ordering = SyncOrdering::kColorAttachment; |
| hazard = access_context.DetectAttachmentHazard(view_gen, AttachmentViewGen::Gen::kRenderArea, load_index, |
| attachment_access, *filtered_view_mask, env.queue_id); |
| aspect = "color"; |
| } else { |
| if (has_depth && (load_index != SYNC_ACCESS_INDEX_NONE)) { |
| attachment_access.ordering = SyncOrdering::kDepthStencilAttachment; |
| hazard = |
| access_context.DetectAttachmentHazard(view_gen, AttachmentViewGen::Gen::kDepthOnlyRenderArea, load_index, |
| attachment_access, *filtered_view_mask, env.queue_id); |
| aspect = "depth"; |
| } |
| if (!hazard.IsHazard() && has_stencil && (stencil_load_index != SYNC_ACCESS_INDEX_NONE)) { |
| attachment_access.ordering = SyncOrdering::kDepthStencilAttachment; |
| hazard = access_context.DetectAttachmentHazard(view_gen, AttachmentViewGen::Gen::kStencilOnlyRenderArea, |
| stencil_load_index, attachment_access, *filtered_view_mask, |
| env.queue_id); |
| aspect = "stencil"; |
| checked_stencil = true; |
| } |
| } |
| |
| if (hazard.IsHazard()) { |
| const VkAttachmentLoadOp load_op = checked_stencil ? ci.stencilLoadOp : ci.loadOp; |
| const SyncValidator& validator = env.validator; |
| const LogObjectList objlist = BaseObjectList(env, cb_context, rp_state.Handle()); |
| |
| std::ostringstream ss; |
| ss << "the " << aspect << " aspect of attachment " << i; |
| ss << " (" << validator.FormatHandle(view_gen.GetViewState()->Handle()) << ")"; |
| ss << " in subpass " << subpass; |
| ss << " of " << validator.FormatHandle(rp_state.Handle()); |
| ss << " (loadOp " << string_VkAttachmentLoadOp(load_op) << ")"; |
| const std::string resource_description = ss.str(); |
| |
| if (hazard.Tag() == kInvalidTag) { // Hazard vs. ILT |
| const std::string error = validator.error_messages_.RenderPassLoadOpVsLayoutTransitionError( |
| env, hazard, cb_context, replay_tag, loc, resource_description, load_op, is_color); |
| skip |= validator.SyncError(hazard.Hazard(), objlist, loc, error); |
| } else { |
| const std::string error = validator.error_messages_.RenderPassLoadOpError( |
| env, hazard, cb_context, replay_tag, loc, resource_description, subpass, i, load_op, is_color); |
| skip |= validator.SyncError(hazard.Hazard(), objlist, loc, error); |
| } |
| } |
| } |
| } |
| return skip; |
| } |
| |
| // Store operation validation can ignore resolve (before it) and layout tranistions after it. The first is ignored |
| // because of the ordering guarantees w.r.t. sample access and that the resolve validation hasn't altered the state, because |
| // store is part of the same Next/End operation. |
| // The latter is handled in layout transistion validation directly |
| bool RenderPassAccessContext::ValidateStoreOperation(const SyncEnvironment& env, const CommandBufferContext& cb_context, |
| ResourceUsageTag replay_tag, const Location& loc) const { |
| bool skip = false; |
| |
| const AttachmentAccess attachment_access = GetAttachmentAccess(SyncOrdering::kRaster, AttachmentAccessType::StoreOp); |
| const uint32_t view_mask = rp_state_->create_info.pSubpasses[current_subpass_].viewMask; |
| |
| for (uint32_t i = 0; i < rp_state_->create_info.attachmentCount; i++) { |
| if (auto filtered_view_mask = FilterViewMask(view_mask, current_subpass_, rp_state_->attachment_last_subpass[i])) { |
| const AttachmentViewGen& view_gen = attachment_views_[i]; |
| const auto& ci = rp_state_->create_info.pAttachments[i]; |
| |
| // The spec states that "don't care" is an operation with VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, |
| // so we assume that an implementation is *free* to write in that case, meaning that for correctness |
| // sake, we treat DONT_CARE as writing. |
| const bool has_depth = vkuFormatHasDepth(ci.format); |
| const bool has_stencil = vkuFormatHasStencil(ci.format); |
| const bool is_color = !(has_depth || has_stencil); |
| const bool store_op_stores = ci.storeOp != VK_ATTACHMENT_STORE_OP_NONE; |
| if (!has_stencil && !store_op_stores) continue; |
| |
| HazardResult hazard; |
| const char* aspect = nullptr; |
| bool checked_stencil = false; |
| if (is_color) { |
| hazard = CurrentContext().DetectAttachmentHazard(view_gen, AttachmentViewGen::Gen::kRenderArea, |
| SYNC_COLOR_ATTACHMENT_OUTPUT_COLOR_ATTACHMENT_WRITE, |
| attachment_access, *filtered_view_mask, env.queue_id); |
| aspect = "color"; |
| } else { |
| const bool stencil_op_stores = ci.stencilStoreOp != VK_ATTACHMENT_STORE_OP_NONE; |
| if (has_depth && store_op_stores) { |
| hazard = CurrentContext().DetectAttachmentHazard(view_gen, AttachmentViewGen::Gen::kDepthOnlyRenderArea, |
| SYNC_LATE_FRAGMENT_TESTS_DEPTH_STENCIL_ATTACHMENT_WRITE, |
| attachment_access, *filtered_view_mask, env.queue_id); |
| aspect = "depth"; |
| } |
| if (!hazard.IsHazard() && has_stencil && stencil_op_stores) { |
| hazard = CurrentContext().DetectAttachmentHazard(view_gen, AttachmentViewGen::Gen::kStencilOnlyRenderArea, |
| SYNC_LATE_FRAGMENT_TESTS_DEPTH_STENCIL_ATTACHMENT_WRITE, |
| attachment_access, *filtered_view_mask, env.queue_id); |
| aspect = "stencil"; |
| checked_stencil = true; |
| } |
| } |
| |
| if (hazard.IsHazard()) { |
| const SyncValidator& validator = env.validator; |
| const char* const op_type_string = checked_stencil ? "stencilStoreOp" : "storeOp"; |
| const VkAttachmentStoreOp store_op = checked_stencil ? ci.stencilStoreOp : ci.storeOp; |
| const LogObjectList objlist = BaseObjectList(env, cb_context, rp_state_->Handle()); |
| |
| std::ostringstream ss; |
| ss << validator.FormatHandle(view_gen.GetViewState()->Handle()); |
| ss << " (subpass " << current_subpass_ << " of " << validator.FormatHandle(rp_state_->Handle()); |
| ss << ", attachment " << i; |
| ss << ", aspect " << aspect << " during store with " << op_type_string; |
| ss << " " << string_VkAttachmentStoreOp(store_op) << ")"; |
| const std::string resource_description = ss.str(); |
| |
| const std::string error = validator.error_messages_.RenderPassStoreOpError(env, hazard, cb_context, replay_tag, loc, |
| resource_description, store_op); |
| skip |= validator.SyncError(hazard.Hazard(), objlist, loc, error); |
| } |
| } |
| } |
| return skip; |
| } |
| |
| // Traverse the attachment resolves for this a specific subpass, and do action() to them. |
| // Used by both validation and record operations |
| // |
| // The signature for Action() reflect the needs of both uses. |
| template <typename Action> |
| void ResolveOperation(Action& action, const vvl::RenderPass& rp_state, const AttachmentViewGenVector& attachment_views, |
| uint32_t render_pass_instance_id, uint32_t subpass) { |
| const auto& rp_ci = rp_state.create_info; |
| const auto* attachment_ci = rp_ci.pAttachments; |
| const auto& subpass_ci = rp_ci.pSubpasses[subpass]; |
| |
| AttachmentAccess attachment_access; |
| attachment_access.render_pass_instance_id = render_pass_instance_id; |
| attachment_access.subpass = subpass; |
| |
| // Color resolve requires an inuse color attachment and a matching inuse resolve attachment |
| if (subpass_ci.pResolveAttachments && subpass_ci.pColorAttachments) { |
| attachment_access.ordering = SyncOrdering::kColorAttachment; |
| for (uint32_t i = 0; i < subpass_ci.colorAttachmentCount; i++) { |
| const uint32_t color_attach = subpass_ci.pColorAttachments[i].attachment; |
| const uint32_t resolve_attach = subpass_ci.pResolveAttachments[i].attachment; |
| if (color_attach != VK_ATTACHMENT_UNUSED && resolve_attach != VK_ATTACHMENT_UNUSED) { |
| attachment_access.type = AttachmentAccessType::ResolveRead; |
| action("color", "resolve read", color_attach, resolve_attach, attachment_views[color_attach], |
| AttachmentViewGen::Gen::kRenderArea, SYNC_COLOR_ATTACHMENT_OUTPUT_COLOR_ATTACHMENT_READ, attachment_access); |
| |
| attachment_access.type = AttachmentAccessType::ResolveWrite; |
| action("color", "resolve write", color_attach, resolve_attach, attachment_views[resolve_attach], |
| AttachmentViewGen::Gen::kRenderArea, SYNC_COLOR_ATTACHMENT_OUTPUT_COLOR_ATTACHMENT_WRITE, attachment_access); |
| } |
| } |
| } |
| |
| // Depth stencil resolve only if the extension is present |
| const auto ds_resolve = vku::FindStructInPNextChain<VkSubpassDescriptionDepthStencilResolve>(subpass_ci.pNext); |
| if (ds_resolve && ds_resolve->pDepthStencilResolveAttachment && |
| (ds_resolve->pDepthStencilResolveAttachment->attachment != VK_ATTACHMENT_UNUSED) && subpass_ci.pDepthStencilAttachment && |
| (subpass_ci.pDepthStencilAttachment->attachment != VK_ATTACHMENT_UNUSED)) { |
| const auto src_at = subpass_ci.pDepthStencilAttachment->attachment; |
| const auto src_ci = attachment_ci[src_at]; |
| // The formats are required to match so we can pick either |
| const bool resolve_depth = (ds_resolve->depthResolveMode != VK_RESOLVE_MODE_NONE) && vkuFormatHasDepth(src_ci.format); |
| const bool resolve_stencil = (ds_resolve->stencilResolveMode != VK_RESOLVE_MODE_NONE) && vkuFormatHasStencil(src_ci.format); |
| const auto dst_at = ds_resolve->pDepthStencilResolveAttachment->attachment; |
| |
| // Figure out which aspects are actually touched during resolve operations |
| const char* aspect_string = nullptr; |
| AttachmentViewGen::Gen gen_type = AttachmentViewGen::Gen::kRenderArea; |
| if (resolve_depth && resolve_stencil) { |
| aspect_string = "depth/stencil"; |
| } else if (resolve_depth) { |
| // Validate depth only |
| gen_type = AttachmentViewGen::Gen::kDepthOnlyRenderArea; |
| aspect_string = "depth"; |
| } else if (resolve_stencil) { |
| // Validate all stencil only |
| gen_type = AttachmentViewGen::Gen::kStencilOnlyRenderArea; |
| aspect_string = "stencil"; |
| } |
| |
| if (aspect_string) { |
| attachment_access.ordering = SyncOrdering::kRaster; |
| |
| attachment_access.type = AttachmentAccessType::ResolveRead; |
| action(aspect_string, "resolve read", src_at, dst_at, attachment_views[src_at], gen_type, |
| SYNC_COLOR_ATTACHMENT_OUTPUT_COLOR_ATTACHMENT_READ, attachment_access); |
| |
| attachment_access.type = AttachmentAccessType::ResolveWrite; |
| action(aspect_string, "resolve write", src_at, dst_at, attachment_views[dst_at], gen_type, |
| SYNC_COLOR_ATTACHMENT_OUTPUT_COLOR_ATTACHMENT_WRITE, attachment_access); |
| } |
| } |
| } |
| |
| bool RenderPassAccessContext::ValidateResolveOperations(const SyncEnvironment& env, const CommandBufferContext& cb_context, |
| ResourceUsageTag replay_tag, const Location& loc) const { |
| const uint32_t view_mask = rp_state_->create_info.pSubpasses[current_subpass_].viewMask; |
| ValidateResolveAction validate_action(env, rp_state_->Handle(), current_subpass_, view_mask, CurrentContext(), cb_context, |
| replay_tag, loc); |
| ResolveOperation(validate_action, *rp_state_, attachment_views_, render_pass_instance_id_, current_subpass_); |
| return validate_action.GetSkip(); |
| } |
| |
| void RenderPassAccessContext::UpdateAttachmentResolveAccess(const vvl::RenderPass& rp_state, |
| const AttachmentViewGenVector& attachment_views, |
| uint32_t render_pass_instance_id, uint32_t subpass, uint32_t view_mask, |
| const ResourceUsageTag tag, AccessContext& access_context, |
| QueueId queue_id) { |
| UpdateStateResolveAction update(access_context, view_mask, tag, queue_id); |
| ResolveOperation(update, rp_state, attachment_views, render_pass_instance_id, subpass); |
| } |
| |
| void RenderPassAccessContext::UpdateAttachmentStoreAccess(const vvl::RenderPass& rp_state, |
| const AttachmentViewGenVector& attachment_views, |
| uint32_t render_pass_instance_id, uint32_t subpass, uint32_t view_mask, |
| const ResourceUsageTag tag, AccessContext& access_context, |
| QueueId queue_id) { |
| AttachmentAccess attachment_access; |
| attachment_access.type = AttachmentAccessType::StoreOp; |
| attachment_access.ordering = SyncOrdering::kRaster; |
| attachment_access.render_pass_instance_id = render_pass_instance_id; |
| attachment_access.subpass = subpass; |
| |
| for (uint32_t i = 0; i < rp_state.create_info.attachmentCount; i++) { |
| if (auto filtered_view_mask = FilterViewMask(view_mask, subpass, rp_state.attachment_last_subpass[i])) { |
| const auto& view_gen = attachment_views[i]; |
| |
| const auto& ci = rp_state.create_info.pAttachments[i]; |
| const bool has_depth = vkuFormatHasDepth(ci.format); |
| const bool has_stencil = vkuFormatHasStencil(ci.format); |
| const bool is_color = !(has_depth || has_stencil); |
| const bool store_op_stores = ci.storeOp != VK_ATTACHMENT_STORE_OP_NONE; |
| |
| if (is_color && store_op_stores) { |
| access_context.UpdateAttachmentAccessState(view_gen, AttachmentViewGen::Gen::kRenderArea, |
| SYNC_COLOR_ATTACHMENT_OUTPUT_COLOR_ATTACHMENT_WRITE, attachment_access, |
| ResourceUsageTagEx{tag}, *filtered_view_mask, queue_id); |
| } else { |
| if (has_depth && store_op_stores) { |
| access_context.UpdateAttachmentAccessState(view_gen, AttachmentViewGen::Gen::kDepthOnlyRenderArea, |
| SYNC_LATE_FRAGMENT_TESTS_DEPTH_STENCIL_ATTACHMENT_WRITE, |
| attachment_access, ResourceUsageTagEx{tag}, *filtered_view_mask, |
| queue_id); |
| } |
| const bool stencil_op_stores = ci.stencilStoreOp != VK_ATTACHMENT_STORE_OP_NONE; |
| if (has_stencil && stencil_op_stores) { |
| access_context.UpdateAttachmentAccessState(view_gen, AttachmentViewGen::Gen::kStencilOnlyRenderArea, |
| SYNC_LATE_FRAGMENT_TESTS_DEPTH_STENCIL_ATTACHMENT_WRITE, |
| attachment_access, ResourceUsageTagEx{tag}, *filtered_view_mask, |
| queue_id); |
| } |
| } |
| } |
| } |
| } |
| |
| void RenderPassAccessContext::RecordLayoutTransitions(const vvl::RenderPass& rp_state, uint32_t subpass, |
| const AttachmentViewGenVector& attachment_views, const ResourceUsageTag tag, |
| AccessContext& access_context) { |
| const auto& transitions = rp_state.subpass_transitions[subpass]; |
| for (const auto& transition : transitions) { |
| const auto& view_gen = attachment_views[transition.attachment]; |
| const SubpassBarrier& subpass_barrier = access_context.GetSubpassBarrier(transition.src_subpass); |
| const AccessContext& src_subpass_context = *subpass_barrier.src_subpass_context; |
| |
| // Import the attachments into the current context |
| ApplySubpassTransitionBarrierAction barrier_action(subpass_barrier, tag); |
| ImageRangeGen attachment_gen = view_gen.GetRangeGen(AttachmentViewGen::Gen::kViewSubresource); |
| access_context.ResolveFromSubpassContext(barrier_action, src_subpass_context, attachment_gen); |
| } |
| } |
| |
| static uint32_t GetSubpassDepthStencilAttachmentIndex(const vku::safe_VkPipelineDepthStencilStateCreateInfo* pipe_ds_ci, |
| const vku::safe_VkAttachmentReference2* depth_stencil_ref) { |
| return (pipe_ds_ci && depth_stencil_ref) ? depth_stencil_ref->attachment : VK_ATTACHMENT_UNUSED; |
| } |
| |
| bool RenderPassAccessContext::ValidateDrawSubpassAttachment(const SyncEnvironment& env, const CommandBufferContext& cb_context, |
| ResourceUsageTag replay_tag, const Location& loc, |
| const vvl::Pipeline* pipeline, bool depth_write_enabled, |
| bool stencil_write_enabled) const { |
| bool skip = false; |
| if (!pipeline || pipeline->RasterizationDisabled()) { |
| return skip; |
| } |
| const auto& list = pipeline->fs_writable_output_location_list; |
| const auto& subpass = rp_state_->create_info.pSubpasses[current_subpass_]; |
| const AccessContext& current_context = CurrentContext(); |
| |
| auto report_atachment_hazard = [&env, &cb_context, replay_tag, &loc](const HazardResult& hazard, |
| const vvl::ImageView& attachment_view, |
| std::string_view attachment_description) { |
| const SyncValidator& validator = env.validator; |
| const vvl::Image& attachment_image = *attachment_view.image_state; |
| LogObjectList objlist = BaseObjectList(env, cb_context, attachment_view.Handle()); |
| objlist.add(attachment_image.Handle()); |
| |
| std::ostringstream ss; |
| ss << attachment_description; |
| ss << " (" << validator.FormatHandle(attachment_view.Handle()); |
| ss << ", " << validator.FormatHandle(attachment_image.Handle()) << ")"; |
| const std::string resource_description = ss.str(); |
| |
| const std::string error = |
| validator.error_messages_.RenderPassAttachmentError(env, hazard, cb_context, replay_tag, loc, resource_description); |
| return validator.SyncError(hazard.Hazard(), objlist, loc, error); |
| }; |
| |
| // Subpass's inputAttachment has been done in ValidateDispatchDrawDescriptorSet |
| if (subpass.pColorAttachments && subpass.colorAttachmentCount && !list.empty()) { |
| for (const auto location : list) { |
| if (location >= subpass.colorAttachmentCount || |
| subpass.pColorAttachments[location].attachment == VK_ATTACHMENT_UNUSED) { |
| continue; |
| } |
| const AttachmentAccess attachment_access = GetAttachmentAccess(SyncOrdering::kColorAttachment); |
| const AttachmentViewGen& view_gen = attachment_views_[subpass.pColorAttachments[location].attachment]; |
| HazardResult hazard = current_context.DetectAttachmentHazard(view_gen, AttachmentViewGen::Gen::kRenderArea, |
| SYNC_COLOR_ATTACHMENT_OUTPUT_COLOR_ATTACHMENT_WRITE, |
| attachment_access, subpass.viewMask, env.queue_id); |
| if (hazard.IsHazard()) { |
| std::ostringstream ss; |
| ss << "color attachment " << location << " in subpass " << current_subpass_; |
| const std::string attachment_description = ss.str(); |
| skip |= report_atachment_hazard(hazard, *view_gen.GetViewState(), attachment_description); |
| } |
| } |
| } |
| |
| // TODO: Read operations for both depth and stencil are possible in the future. |
| const auto ds_state = pipeline->DepthStencilState(); |
| const uint32_t depth_stencil_attachment = GetSubpassDepthStencilAttachmentIndex(ds_state, subpass.pDepthStencilAttachment); |
| |
| if (depth_stencil_attachment != VK_ATTACHMENT_UNUSED) { |
| const AttachmentViewGen& view_gen = attachment_views_[depth_stencil_attachment]; |
| const vvl::ImageView& view_state = *view_gen.GetViewState(); |
| const VkFormat ds_format = view_state.create_info.format; |
| const bool depth_write = depth_write_enabled && vkuFormatHasDepth(ds_format); |
| const bool stencil_write = stencil_write_enabled && vkuFormatHasStencil(ds_format); |
| |
| if (depth_write) { |
| const AttachmentAccess attachment_access = GetAttachmentAccess(SyncOrdering::kDepthStencilAttachment); |
| HazardResult hazard = current_context.DetectAttachmentHazard(view_gen, AttachmentViewGen::Gen::kDepthOnlyRenderArea, |
| SYNC_LATE_FRAGMENT_TESTS_DEPTH_STENCIL_ATTACHMENT_WRITE, |
| attachment_access, subpass.viewMask, env.queue_id); |
| if (hazard.IsHazard()) { |
| std::ostringstream ss; |
| ss << "depth aspect of depth-stencil attachment in subpass " << current_subpass_; |
| const std::string attachment_description = ss.str(); |
| skip |= report_atachment_hazard(hazard, view_state, attachment_description); |
| } |
| } |
| if (stencil_write) { |
| const AttachmentAccess attachment_access = GetAttachmentAccess(SyncOrdering::kDepthStencilAttachment); |
| HazardResult hazard = current_context.DetectAttachmentHazard(view_gen, AttachmentViewGen::Gen::kStencilOnlyRenderArea, |
| SYNC_LATE_FRAGMENT_TESTS_DEPTH_STENCIL_ATTACHMENT_WRITE, |
| attachment_access, subpass.viewMask, env.queue_id); |
| if (hazard.IsHazard()) { |
| std::ostringstream ss; |
| ss << "stencil aspect of depth-stencil attachment in subpass " << current_subpass_; |
| const std::string attachment_description = ss.str(); |
| skip |= report_atachment_hazard(hazard, view_state, attachment_description); |
| } |
| } |
| } |
| return skip; |
| } |
| |
| void RenderPassAccessContext::RecordDrawSubpassAttachment(const vvl::Pipeline* pipeline, bool depth_write_enabled, |
| bool stencil_write_enabled, ResourceUsageTag tag, QueueId queue_id) { |
| if (!pipeline || pipeline->RasterizationDisabled()) { |
| return; |
| } |
| const auto& list = pipeline->fs_writable_output_location_list; |
| const auto& subpass = rp_state_->create_info.pSubpasses[current_subpass_]; |
| AccessContext& current_context = CurrentContext(); |
| |
| // Subpass's inputAttachment has been done in RecordDispatchDrawDescriptorSet |
| if (subpass.pColorAttachments && subpass.colorAttachmentCount && !list.empty()) { |
| for (const auto location : list) { |
| if (location >= subpass.colorAttachmentCount || |
| subpass.pColorAttachments[location].attachment == VK_ATTACHMENT_UNUSED) { |
| continue; |
| } |
| const AttachmentAccess attachment_access = GetAttachmentAccess(SyncOrdering::kColorAttachment); |
| const AttachmentViewGen& view_gen = attachment_views_[subpass.pColorAttachments[location].attachment]; |
| current_context.UpdateAttachmentAccessState(view_gen, AttachmentViewGen::Gen::kRenderArea, |
| SYNC_COLOR_ATTACHMENT_OUTPUT_COLOR_ATTACHMENT_WRITE, attachment_access, |
| ResourceUsageTagEx{tag}, subpass.viewMask, queue_id); |
| } |
| } |
| |
| // TODO: Read operations for both depth and stencil are possible in the future |
| const auto* ds_state = pipeline->DepthStencilState(); |
| const uint32_t depth_stencil_attachment = GetSubpassDepthStencilAttachmentIndex(ds_state, subpass.pDepthStencilAttachment); |
| if (depth_stencil_attachment != VK_ATTACHMENT_UNUSED) { |
| const AttachmentViewGen& view_gen = attachment_views_[depth_stencil_attachment]; |
| const vvl::ImageView& view_state = *view_gen.GetViewState(); |
| const VkFormat ds_format = view_state.create_info.format; |
| const bool depth_write = depth_write_enabled && vkuFormatHasDepth(ds_format); |
| const bool stencil_write = stencil_write_enabled && vkuFormatHasStencil(ds_format); |
| |
| if (depth_write || stencil_write) { |
| const AttachmentAccess attachment_access = GetAttachmentAccess(SyncOrdering::kDepthStencilAttachment); |
| const auto ds_gentype = view_gen.GetDepthStencilRenderAreaGenType(depth_write, stencil_write); |
| current_context.UpdateAttachmentAccessState(view_gen, ds_gentype, |
| SYNC_LATE_FRAGMENT_TESTS_DEPTH_STENCIL_ATTACHMENT_WRITE, attachment_access, |
| ResourceUsageTagEx{tag}, subpass.viewMask, queue_id); |
| } |
| } |
| } |
| |
| const vvl::ImageView* RenderPassAccessContext::GetClearAttachmentView(const VkClearAttachment& clear_attachment) const { |
| const auto& subpass = rp_state_->create_info.pSubpasses[current_subpass_]; |
| uint32_t attachment_index = VK_ATTACHMENT_UNUSED; |
| if (clear_attachment.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { |
| if (clear_attachment.colorAttachment < subpass.colorAttachmentCount) { |
| attachment_index = subpass.pColorAttachments[clear_attachment.colorAttachment].attachment; |
| } |
| } else if (clear_attachment.aspectMask & kDepthStencilAspects) { |
| if (subpass.pDepthStencilAttachment) { |
| attachment_index = subpass.pDepthStencilAttachment->attachment; |
| } |
| } |
| // This catches both out of bounds attachment index and VK_ATTACHMENT_UNUSED special value. |
| if (attachment_index >= rp_state_->create_info.attachmentCount) { |
| return nullptr; |
| } |
| return attachment_views_[attachment_index].GetViewState(); |
| } |
| |
| bool RenderPassAccessContext::ValidateNextSubpass(const SyncEnvironment& env, const CommandBufferContext& cb_context, |
| ResourceUsageTag replay_tag, const Location& loc) const { |
| bool skip = false; |
| skip |= ValidateResolveOperations(env, cb_context, replay_tag, loc); |
| skip |= ValidateStoreOperation(env, cb_context, replay_tag, loc); |
| |
| const auto next_subpass = current_subpass_ + 1; |
| if (next_subpass >= rp_state_->create_info.subpassCount) { |
| return skip; |
| } |
| const uint32_t next_subpass_view_mask = rp_state_->create_info.pSubpasses[next_subpass].viewMask; |
| const auto& next_context = subpass_contexts_[next_subpass]; |
| skip |= ValidateLayoutTransitions(env, next_context, *rp_state_, render_pass_instance_id_, next_subpass, next_subpass_view_mask, |
| attachment_views_, cb_context, replay_tag, loc); |
| if (!skip) { |
| // To avoid complex (and buggy) duplication of the affect of layout transitions on load operations, we'll record them |
| // on a copy of the (empty) next context. |
| // Note: The resource access map should be empty so hopefully this copy isn't too horrible from a perf POV. |
| AccessContext temp_context(cb_context.GetSyncState()); |
| temp_context.InitFrom(next_context); |
| RecordLayoutTransitions(*rp_state_, next_subpass, attachment_views_, kInvalidTag, temp_context); |
| skip |= ValidateLoadOperation(env, temp_context, *rp_state_, render_pass_instance_id_, next_subpass, next_subpass_view_mask, |
| attachment_views_, cb_context, replay_tag, loc); |
| } |
| return skip; |
| } |
| |
| bool RenderPassAccessContext::ValidateEndRenderPass(const SyncEnvironment& env, const CommandBufferContext& cb_context, |
| ResourceUsageTag replay_tag, const Location& loc) const { |
| bool skip = false; |
| skip |= ValidateResolveOperations(env, cb_context, replay_tag, loc); |
| skip |= ValidateStoreOperation(env, cb_context, replay_tag, loc); |
| skip |= ValidateFinalSubpassLayoutTransitions(env, cb_context, replay_tag, loc); |
| return skip; |
| } |
| |
| AccessContext* RenderPassAccessContext::CreateStoreResolveProxy(QueueId queue_id) const { |
| return CreateStoreResolveProxyContext(CurrentContext(), *rp_state_, render_pass_instance_id_, current_subpass_, |
| rp_state_->create_info.pSubpasses[current_subpass_].viewMask, attachment_views_, |
| queue_id); |
| } |
| |
| bool RenderPassAccessContext::ValidateFinalSubpassLayoutTransitions(const SyncEnvironment& env, |
| const CommandBufferContext& cb_context, |
| ResourceUsageTag replay_tag, const Location& loc) const { |
| bool skip = false; |
| |
| // As validation methods are const and precede the record/update phase, for any tranistions from the current (last) |
| // subpass, we have to validate them against a copy of the current AccessContext, with resolve operations applied. |
| // Note: we could be more efficient by tracking whether or not we actually *have* any changes (e.g. attachment resolve) |
| // to apply and only copy then, if this proves a hot spot. |
| std::unique_ptr<AccessContext> proxy_for_current; |
| |
| const AccessContext* context = nullptr; |
| |
| // Validate the "finalLayout" transitions to external |
| // Get them from where there we're hidding in the extra entry. |
| const auto& final_transitions = rp_state_->subpass_transitions.back(); |
| for (const auto& transition : final_transitions) { |
| SyncBarrier merged_barrier; |
| const auto& view_gen = attachment_views_[transition.attachment]; |
| if (transition.src_subpass != VK_SUBPASS_EXTERNAL) { |
| const SubpassBarrier& subpass_barrier = subpass_contexts_[transition.src_subpass].GetDstExternalSubpassBarrier(); |
| merged_barrier = SyncBarrier(subpass_barrier.barriers); |
| if (transition.src_subpass != current_subpass_) { |
| context = subpass_barrier.src_subpass_context; |
| } else { |
| if (!proxy_for_current) { |
| // We haven't recorded resolve ofor the current_subpass, so we need to copy current and update it *as if* |
| proxy_for_current.reset(CreateStoreResolveProxy(env.queue_id)); |
| } |
| context = proxy_for_current.get(); |
| } |
| } else { |
| context = external_context_; |
| // NOTE: Unused attachments still transition from initialLayout to finalLayout, but no |
| // subpass uses them, so there is no external subpass dependency barrier to merge. |
| } |
| |
| // Use the merged barrier for the hazard check (safe since it just considers the src (first) scope. |
| auto hazard = context->DetectImageBarrierHazard(view_gen, merged_barrier, AccessContext::DetectOptions::kDetectPrevious, |
| env.queue_id); |
| if (hazard.IsHazard()) { |
| const SyncValidator& validator = env.validator; |
| const LogObjectList objlist = BaseObjectList(env, cb_context, rp_state_->Handle()); |
| |
| std::ostringstream ss; |
| ss << "on attachment " << transition.attachment << " ("; |
| ss << validator.FormatHandle(view_gen.GetViewState()->Handle()); |
| ss << ", " << validator.FormatHandle(view_gen.GetViewState()->image_state->Handle()); |
| ss << ", oldLayout " << string_VkImageLayout(transition.old_layout); |
| ss << ", newLayout " << string_VkImageLayout(transition.new_layout); |
| ss << ")"; |
| const std::string resource_description = ss.str(); |
| |
| if (hazard.Tag() == kInvalidTag) { // Hazard vs. store/resolve |
| const std::string error = validator.error_messages_.RenderPassFinalLayoutTransitionVsStoreOrResolveError( |
| env, hazard, cb_context, replay_tag, loc, resource_description, rp_state_->Handle(), transition.old_layout, |
| transition.new_layout, transition.src_subpass); |
| skip |= validator.SyncError(hazard.Hazard(), objlist, loc, error); |
| } else { |
| const std::string error = validator.error_messages_.RenderPassFinalLayoutTransitionError( |
| env, hazard, cb_context, replay_tag, loc, resource_description, rp_state_->Handle(), transition.old_layout, |
| transition.new_layout); |
| skip |= validator.SyncError(hazard.Hazard(), objlist, loc, error); |
| } |
| } |
| } |
| return skip; |
| } |
| |
| void RenderPassAccessContext::RecordLayoutTransitions(const ResourceUsageTag tag) { |
| RecordLayoutTransitions(*rp_state_, current_subpass_, attachment_views_, tag, CurrentContext()); |
| } |
| |
| void RenderPassAccessContext::RecordLoadOperations(const ResourceUsageTag tag, QueueId queue_id) { |
| const auto* attachment_ci = rp_state_->create_info.pAttachments; |
| auto& subpass_context = CurrentContext(); |
| const uint32_t view_mask = rp_state_->create_info.pSubpasses[current_subpass_].viewMask; |
| |
| for (uint32_t i = 0; i < rp_state_->create_info.attachmentCount; i++) { |
| if (auto filtered_view_mask = FilterViewMask(view_mask, current_subpass_, rp_state_->attachment_first_subpass[i])) { |
| const AttachmentViewGen& view_gen = attachment_views_[i]; |
| |
| const VkAttachmentDescription2& ci = *attachment_ci[i].ptr(); |
| const bool has_depth = vkuFormatHasDepth(ci.format); |
| const bool has_stencil = vkuFormatHasStencil(ci.format); |
| const bool is_color = !(has_depth || has_stencil); |
| |
| if (is_color) { |
| // TODO: LoadOp can access the entire attachment subresource, not only the render area. |
| // The exception is when a feedback loop is enabled for the attachment, then only the render area is accessed. |
| const SyncAccessIndex load_op_access = ColorLoadUsage(ci.loadOp); |
| if (load_op_access != SYNC_ACCESS_INDEX_NONE) { |
| const AttachmentAccess attachment_access = |
| GetAttachmentAccess(SyncOrdering::kColorAttachment, AttachmentAccessType::LoadOp); |
| subpass_context.UpdateAttachmentAccessState(view_gen, AttachmentViewGen::Gen::kRenderArea, load_op_access, |
| attachment_access, ResourceUsageTagEx{tag}, *filtered_view_mask, |
| queue_id); |
| } |
| } else { |
| // TODO: Update depth/stencil aspects separately only if separateDepthStencilAttachmentAccess is defined, |
| // otherwise both should be updated. |
| // Also LoadOp can access the entire attachment subresource, not only the render area. |
| // The exception is when a feedback loop is enabled for the attachment, then only the render area is accessed. |
| const AttachmentAccess attachment_access = |
| GetAttachmentAccess(SyncOrdering::kDepthStencilAttachment, AttachmentAccessType::LoadOp); |
| if (has_depth) { |
| const SyncAccessIndex load_op = DepthStencilLoadUsage(ci.loadOp); |
| if (load_op != SYNC_ACCESS_INDEX_NONE) { |
| subpass_context.UpdateAttachmentAccessState(view_gen, AttachmentViewGen::Gen::kDepthOnlyRenderArea, load_op, |
| attachment_access, ResourceUsageTagEx{tag}, *filtered_view_mask, |
| queue_id); |
| } |
| } |
| if (has_stencil) { |
| const SyncAccessIndex load_op = DepthStencilLoadUsage(ci.stencilLoadOp); |
| if (load_op != SYNC_ACCESS_INDEX_NONE) { |
| subpass_context.UpdateAttachmentAccessState(view_gen, AttachmentViewGen::Gen::kStencilOnlyRenderArea, |
| load_op, attachment_access, ResourceUsageTagEx{tag}, |
| *filtered_view_mask, queue_id); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| AttachmentViewGenVector RenderPassAccessContext::CreateAttachmentViewGen( |
| const VkRect2D& render_area, vvl::span<const std::shared_ptr<const vvl::ImageView>> attachment_views) { |
| AttachmentViewGenVector view_gens; |
| VkExtent3D extent = CastTo3D(render_area.extent); |
| VkOffset3D offset = CastTo3D(render_area.offset); |
| view_gens.reserve(attachment_views.size()); |
| for (const auto& view : attachment_views) { |
| view_gens.emplace_back(view.get(), offset, extent); |
| } |
| return view_gens; |
| } |
| |
| RenderPassAccessContext::RenderPassAccessContext(const vvl::RenderPass& rp_state, const VkRect2D& render_area, |
| VkQueueFlags queue_flags, |
| vvl::span<const std::shared_ptr<const vvl::ImageView>> attachment_views, |
| const AccessContext& external_context, uint32_t render_pass_instance_id, |
| QueueId queue_id) |
| : rp_state_(&rp_state), |
| attachment_views_(CreateAttachmentViewGen(render_area, attachment_views)), |
| external_context_(&external_context), |
| subpass_contexts_(InitSubpassContexts(queue_flags, rp_state, external_context, queue_id)), |
| render_pass_instance_id_(render_pass_instance_id), |
| current_subpass_(0) {} |
| |
| void RenderPassAccessContext::RecordBeginRenderPass(const ResourceUsageTag transition_tag, const ResourceUsageTag load_op_tag, |
| QueueId queue_id) { |
| AccessContext& current_context = CurrentContext(); |
| current_context.SetStartTag(transition_tag); |
| |
| RecordLayoutTransitions(transition_tag); |
| RecordLoadOperations(load_op_tag, queue_id); |
| } |
| |
| bool RenderPassAccessContext::AdvanceSubpass() { |
| if (current_subpass_ + 1 >= rp_state_->create_info.subpassCount) { |
| return false; |
| } |
| current_subpass_++; |
| return true; |
| } |
| |
| void RenderPassAccessContext::RecordNextSubpass(ResourceUsageTag resolve_tag, const ResourceUsageTag store_tag, |
| const ResourceUsageTag transition_tag, const ResourceUsageTag load_tag, |
| QueueId queue_id) { |
| // AdvanceSubpass must already be called |
| assert(current_subpass_ > 0); |
| |
| // The resolve and store are logically part of the previous subpass |
| const uint32_t prev_subpass = current_subpass_ - 1; |
| const uint32_t view_mask = rp_state_->create_info.pSubpasses[prev_subpass].viewMask; |
| UpdateAttachmentResolveAccess(*rp_state_, attachment_views_, render_pass_instance_id_, prev_subpass, view_mask, resolve_tag, |
| subpass_contexts_[prev_subpass], queue_id); |
| UpdateAttachmentStoreAccess(*rp_state_, attachment_views_, render_pass_instance_id_, prev_subpass, view_mask, store_tag, |
| subpass_contexts_[prev_subpass], queue_id); |
| |
| // Layout transition and load are from the current subpass |
| AccessContext& current_context = CurrentContext(); |
| current_context.SetStartTag(transition_tag); |
| RecordLayoutTransitions(transition_tag); |
| RecordLoadOperations(load_tag, queue_id); |
| } |
| |
| void RenderPassAccessContext::RecordEndRenderPass(AccessContext& external_context, const ResourceUsageTag store_tag, |
| const ResourceUsageTag transition_tag, QueueId queue_id) { |
| const uint32_t view_mask = rp_state_->create_info.pSubpasses[current_subpass_].viewMask; |
| |
| // Add the resolve and store accesses |
| UpdateAttachmentResolveAccess(*rp_state_, attachment_views_, render_pass_instance_id_, current_subpass_, view_mask, store_tag, |
| CurrentContext(), queue_id); |
| UpdateAttachmentStoreAccess(*rp_state_, attachment_views_, render_pass_instance_id_, current_subpass_, view_mask, store_tag, |
| CurrentContext(), queue_id); |
| |
| // Export the accesses from the renderpass... |
| external_context.ResolveChildContexts(GetSubpassContexts()); |
| |
| // Add the "finalLayout" transitions to external |
| // Get them from where there we're hidding in the extra entry. |
| // Not that since *final* always comes from *one* subpass per view, we don't have to accumulate the barriers |
| // TODO Aliasing we may need to reconsider barrier accumulation... though I don't know that it would be valid for aliasing |
| // that had mulitple final layout transistions from mulitple final subpasses. |
| const auto& final_transitions = rp_state_->subpass_transitions.back(); |
| for (const auto& transition : final_transitions) { |
| const AttachmentViewGen& view_gen = attachment_views_[transition.attachment]; |
| ImageRangeGen range_gen = view_gen.GetRangeGen(AttachmentViewGen::Gen::kViewSubresource); |
| |
| ImageRangeGen markup_range_gen = range_gen; // second copy, preserve range_gen to use later |
| ApplyMarkupFunctor markup_action(true); |
| external_context.UpdateMemoryAccessState(markup_action, markup_range_gen); |
| |
| PendingBarriers pending_barriers; |
| if (transition.src_subpass != VK_SUBPASS_EXTERNAL) { |
| const SubpassBarrier& dst_external_barrier = subpass_contexts_[transition.src_subpass].GetDstExternalSubpassBarrier(); |
| assert(&subpass_contexts_[transition.src_subpass] == dst_external_barrier.src_subpass_context); |
| for (const auto& barrier : dst_external_barrier.barriers) { |
| const BarrierScope barrier_scope(barrier, queue_id); |
| CollectBarriersFunctor collect_barriers(external_context, barrier_scope, barrier, true, true, vvl::kNoIndex32, |
| pending_barriers); |
| external_context.UpdateMemoryAccessState(collect_barriers, range_gen); |
| } |
| } else { |
| // Unused attachments still transition from initialLayout to finalLayout, but no subpass |
| // uses them, so record the transition without applying a subpass dependency barrier. |
| const SyncBarrier empty_barrier; |
| CollectBarriersFunctor collect_barriers(external_context, BarrierScope(empty_barrier, queue_id), empty_barrier, true, |
| true, vvl::kNoIndex32, pending_barriers); |
| external_context.UpdateMemoryAccessState(collect_barriers, range_gen); |
| } |
| pending_barriers.Apply(transition_tag); |
| } |
| } |
| |
| AccessContext& RenderPassAccessContext::CurrentContext() { |
| assert(current_subpass_ < rp_state_->create_info.subpassCount); |
| return subpass_contexts_[current_subpass_]; |
| } |
| |
| const AccessContext& RenderPassAccessContext::CurrentContext() const { |
| assert(current_subpass_ < rp_state_->create_info.subpassCount); |
| return subpass_contexts_[current_subpass_]; |
| } |
| |
| vvl::span<const AccessContext> RenderPassAccessContext::GetSubpassContexts() const { |
| return vvl::make_span<const AccessContext>(subpass_contexts_.get(), rp_state_->create_info.subpassCount); |
| } |
| |
| vvl::span<AccessContext> RenderPassAccessContext::GetSubpassContexts() { |
| return vvl::make_span<AccessContext>(subpass_contexts_.get(), rp_state_->create_info.subpassCount); |
| } |
| |
| AttachmentAccess RenderPassAccessContext::GetAttachmentAccess(SyncOrdering ordering, AttachmentAccessType type) const { |
| AttachmentAccess attachment_access; |
| attachment_access.type = type; |
| attachment_access.ordering = ordering; |
| attachment_access.render_pass_instance_id = render_pass_instance_id_; |
| attachment_access.subpass = current_subpass_; |
| return attachment_access; |
| } |
| |
| } // namespace syncval |