syncval: Add loadOp validation to renderpass
Add state tracker data for loadOp validation and implement for
renderpass operations.
Change-Id: Ie15ddcb4dda7645bd8944528b369cab2e48f16b3
diff --git a/layers/synchronization_validation.cpp b/layers/synchronization_validation.cpp
index 6634d4b..69ce024 100644
--- a/layers/synchronization_validation.cpp
+++ b/layers/synchronization_validation.cpp
@@ -355,6 +355,17 @@
static bool SimpleBinding(const BINDABLE &bindable) { return !bindable.sparse && bindable.binding.mem_state; }
+static SyncStageAccessIndex ColorLoadUsage(VkAttachmentLoadOp load_op) {
+ const auto stage_access = (load_op == VK_ATTACHMENT_LOAD_OP_LOAD) ? SYNC_COLOR_ATTACHMENT_OUTPUT_COLOR_ATTACHMENT_READ
+ : SYNC_COLOR_ATTACHMENT_OUTPUT_COLOR_ATTACHMENT_WRITE;
+ return stage_access;
+}
+static SyncStageAccessIndex DepthStencilLoadUsage(VkAttachmentLoadOp load_op) {
+ const auto stage_access = (load_op == VK_ATTACHMENT_LOAD_OP_LOAD) ? SYNC_EARLY_FRAGMENT_TESTS_DEPTH_STENCIL_ATTACHMENT_READ
+ : SYNC_EARLY_FRAGMENT_TESTS_DEPTH_STENCIL_ATTACHMENT_WRITE;
+ return stage_access;
+}
+
void AccessContext::ResolvePreviousAccess(const IMAGE_STATE &image_state, const VkImageSubresourceRange &subresource_range_arg,
AddressType address_type, ResourceAccessRangeMap *descent_map,
const ResourceAccessState *infill_state) const {
@@ -369,13 +380,13 @@
}
}
-static bool ValidateLayoutTransitions(const SyncValidator &sync_state, const RENDER_PASS_STATE &rp_state,
- const std::vector<const IMAGE_VIEW_STATE *> &attachment_views, const char *func_name,
- uint32_t subpass, const AccessContext &context) {
+bool AccessContext::ValidateLayoutTransitions(const SyncValidator &sync_state, const RENDER_PASS_STATE &rp_state,
+ const std::vector<const IMAGE_VIEW_STATE *> &attachment_views, const char *func_name,
+ uint32_t subpass) const {
bool skip = false;
const auto &transitions = rp_state.subpass_transitions[subpass];
for (const auto &transition : transitions) {
- auto hazard = context.DetectSubpassTransitionHazard(transition, attachment_views);
+ auto hazard = DetectSubpassTransitionHazard(transition, attachment_views);
if (hazard.hazard) {
skip |= sync_state.LogError(rp_state.renderPass, string_SyncHazardVUID(hazard.hazard),
"%s: Hazard %s in subpass %" PRIu32 " for attachment %" PRIu32 " image layout transition.",
@@ -385,6 +396,103 @@
return skip;
}
+bool AccessContext::ValidateLoadOperation(const SyncValidator &sync_state, const RENDER_PASS_STATE &rp_state,
+ const VkRect2D &render_area,
+ const std::vector<const IMAGE_VIEW_STATE *> &attachment_views, const char *func_name,
+ uint32_t subpass) const {
+ bool skip = false;
+ const auto *attachment_ci = rp_state.createInfo.pAttachments;
+ VkExtent3D extent = CastTo3D(render_area.extent);
+ VkOffset3D offset = CastTo3D(render_area.offset);
+ const auto external_access_scope = src_external_.barrier.dst_access_scope;
+ HazardResult hazard;
+
+ for (uint32_t i = 0; i < rp_state.createInfo.attachmentCount; i++) {
+ if (subpass == rp_state.attachment_first_subpass[i]) {
+ if (attachment_views[i] == nullptr) continue;
+ const IMAGE_VIEW_STATE &view = *attachment_views[i];
+ const IMAGE_STATE *image = view.image_state.get();
+ if (image == nullptr) continue;
+ const auto &ci = attachment_ci[i];
+ const bool is_transition = rp_state.attachment_first_is_transition[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 = FormatHasDepth(ci.format);
+ const bool has_stencil = FormatHasDepth(ci.format);
+ const bool is_color = !(has_depth || has_stencil);
+
+ const SyncStageAccessIndex load_index = has_depth ? DepthStencilLoadUsage(ci.loadOp) : ColorLoadUsage(ci.loadOp);
+ const SyncStageAccessFlags load_mask = (has_depth || is_color) ? SyncStageAccess::Flags(load_index) : 0U;
+ const SyncStageAccessIndex stencil_load_index = has_stencil ? DepthStencilLoadUsage(ci.stencilLoadOp) : load_index;
+ const SyncStageAccessFlags stencil_mask = has_stencil ? SyncStageAccess::Flags(stencil_load_index) : 0U;
+
+ const char *aspect = nullptr;
+ if (is_transition) {
+ // For transition w
+ SyncHazard transition_hazard = SyncHazard::NONE;
+ bool checked_stencil = false;
+ if (load_mask) {
+ if ((load_mask & external_access_scope) != load_mask) {
+ transition_hazard =
+ SyncStageAccess::HasWrite(load_mask) ? SyncHazard::WRITE_AFTER_WRITE : SyncHazard::READ_AFTER_WRITE;
+ aspect = is_color ? "color" : "depth";
+ }
+ if (!transition_hazard && stencil_mask) {
+ if ((stencil_mask & external_access_scope) != stencil_mask) {
+ transition_hazard = SyncStageAccess::HasWrite(stencil_mask) ? SyncHazard::WRITE_AFTER_WRITE
+ : SyncHazard::READ_AFTER_WRITE;
+ aspect = "stencil";
+ checked_stencil = true;
+ }
+ }
+ }
+ if (transition_hazard) {
+ // Hazard vs. ILT
+ auto load_op_string = string_VkAttachmentLoadOp(checked_stencil ? ci.stencilLoadOp : ci.loadOp);
+ skip |=
+ sync_state.LogError(rp_state.renderPass, string_SyncHazardVUID(hazard.hazard),
+ "%s: Hazard %s vs. layout transition in subpass %" PRIu32 " for attachment %" PRIu32
+ " aspect %s during load with loadOp %s.",
+ func_name, string_SyncHazard(transition_hazard), subpass, i, aspect, load_op_string);
+ }
+ } else {
+ auto hazard_range = view.normalized_subresource_range;
+ bool checked_stencil = false;
+ if (is_color) {
+ hazard = DetectHazard(*image, load_index, view.normalized_subresource_range, offset, extent);
+ aspect = "color";
+ } else {
+ if (has_depth) {
+ hazard_range.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
+ hazard = DetectHazard(*image, load_index, hazard_range, offset, extent);
+ aspect = "depth";
+ }
+ if (!hazard.hazard && has_stencil) {
+ hazard_range.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT;
+ hazard = DetectHazard(*image, stencil_load_index, hazard_range, offset, extent);
+ aspect = "stencil";
+ checked_stencil = true;
+ }
+ }
+
+ if (hazard.hazard) {
+ auto load_op_string = string_VkAttachmentLoadOp(checked_stencil ? ci.stencilLoadOp : ci.loadOp);
+ skip |= sync_state.LogError(rp_state.renderPass, string_SyncHazardVUID(hazard.hazard),
+ "%s: Hazard %s in subpass %" PRIu32 " for attachment %" PRIu32
+ " aspect %s during load with loadOp %s.",
+ func_name, string_SyncHazard(hazard.hazard), subpass, i, aspect, load_op_string);
+ }
+ }
+ }
+ }
+ return skip;
+}
+
class HazardDetector {
SyncStageAccessIndex usage_index_;
@@ -411,10 +519,15 @@
HazardResult AccessContext::DetectHazard(const IMAGE_STATE &image, SyncStageAccessIndex current_usage,
const VkImageSubresourceLayers &subresource, const VkOffset3D &offset,
const VkExtent3D &extent) const {
- if (!SimpleBinding(image)) return HazardResult();
- // TODO: replace the encoder/generator with offset3D/extent3D aware versions
VkImageSubresourceRange subresource_range = {subresource.aspectMask, subresource.mipLevel, 1, subresource.baseArrayLayer,
subresource.layerCount};
+ return DetectHazard(image, current_usage, subresource_range, offset, extent);
+}
+
+HazardResult AccessContext::DetectHazard(const IMAGE_STATE &image, SyncStageAccessIndex current_usage,
+ const VkImageSubresourceRange &subresource_range, const VkOffset3D &offset,
+ const VkExtent3D &extent) const {
+ if (!SimpleBinding(image)) return HazardResult();
subresource_adapter::ImageRangeGenerator range_gen(*image.fragment_encoder.get(), subresource_range, offset, extent);
const auto address_type = ImageAddressType(image);
const auto base_address = ResourceBaseAddress(image);
@@ -787,7 +900,8 @@
views[transition.attachment] = sync_state_->Get<IMAGE_VIEW_STATE>(attachments[transition.attachment]);
}
- skip |= ValidateLayoutTransitions(*sync_state_, rp_state, views, func_name, 0, temp_context);
+ skip |= temp_context.ValidateLayoutTransitions(*sync_state_, rp_state, views, func_name, 0);
+ skip |= temp_context.ValidateLoadOperation(*sync_state_, rp_state, pRenderPassBegin->renderArea, views, func_name, 0);
}
return skip;
}
@@ -796,7 +910,8 @@
// TODO: Things to add here.
// Validate Preserve/Resolve attachments
bool skip = false;
- skip |= current_renderpass_context_->ValidateNextSubpassLayoutTransitions(*sync_state_, func_name);
+ skip |=
+ current_renderpass_context_->ValidateNextSubpass(*sync_state_, cb_state_->activeRenderPassBeginInfo.renderArea, func_name);
return skip;
}
@@ -823,7 +938,7 @@
void CommandBufferAccessContext::RecordNextSubpass(const RENDER_PASS_STATE &rp_state, const ResourceUsageTag &tag) {
assert(current_renderpass_context_);
- current_renderpass_context_->RecordNextSubpass(tag);
+ current_renderpass_context_->RecordNextSubpass(cb_state_->activeRenderPassBeginInfo.renderArea, tag);
current_context_ = ¤t_renderpass_context_->CurrentContext();
}
@@ -837,11 +952,13 @@
current_renderpass_context_ = nullptr;
}
-bool RenderPassAccessContext::ValidateNextSubpassLayoutTransitions(const SyncValidator &sync_state, const char *func_name) const {
+bool RenderPassAccessContext::ValidateNextSubpass(const SyncValidator &sync_state, const VkRect2D &render_area,
+ const char *func_name) const {
bool skip = false;
const auto next_subpass = current_subpass_ + 1;
- skip |= ValidateLayoutTransitions(sync_state, *rp_state_, attachment_views_, func_name, next_subpass,
- subpass_contexts_[next_subpass]);
+ const auto &next_context = subpass_contexts_[next_subpass];
+ skip |= next_context.ValidateLayoutTransitions(sync_state, *rp_state_, attachment_views_, func_name, next_subpass);
+ skip |= next_context.ValidateLoadOperation(sync_state, *rp_state_, render_area, attachment_views_, func_name, next_subpass);
return skip;
}
@@ -895,6 +1012,43 @@
}
}
+void RenderPassAccessContext::RecordLoadOperations(const VkRect2D &render_area, const ResourceUsageTag &tag) {
+ const auto *attachment_ci = rp_state_->createInfo.pAttachments;
+ auto &subpass_context = subpass_contexts_[current_subpass_];
+ VkExtent3D extent = CastTo3D(render_area.extent);
+ VkOffset3D offset = CastTo3D(render_area.offset);
+
+ for (uint32_t i = 0; i < rp_state_->createInfo.attachmentCount; i++) {
+ if (rp_state_->attachment_first_subpass[i] == current_subpass_) {
+ if (attachment_views_[i] == nullptr) continue; // UNUSED
+ const auto &view = *attachment_views_[i];
+ const IMAGE_STATE *image = view.image_state.get();
+ if (image == nullptr) continue;
+
+ const auto &ci = attachment_ci[i];
+ const bool has_depth = FormatHasDepth(ci.format);
+ const bool has_stencil = FormatHasDepth(ci.format);
+ const bool is_color = !(has_depth || has_stencil);
+
+ if (is_color) {
+ subpass_context.UpdateAccessState(*image, ColorLoadUsage(ci.loadOp), view.normalized_subresource_range, offset,
+ extent, tag);
+ } else {
+ auto update_range = view.normalized_subresource_range;
+ if (has_depth) {
+ update_range.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
+ subpass_context.UpdateAccessState(*image, DepthStencilLoadUsage(ci.loadOp), update_range, offset, extent, tag);
+ }
+ if (has_stencil) {
+ update_range.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT;
+ subpass_context.UpdateAccessState(*image, DepthStencilLoadUsage(ci.stencilLoadOp), update_range, offset, extent,
+ tag);
+ }
+ }
+ }
+ }
+}
+
void RenderPassAccessContext::RecordBeginRenderPass(const SyncValidator &state, const CMD_BUFFER_STATE &cb_state,
VkQueueFlags queue_flags, const ResourceUsageTag &tag) {
current_subpass_ = 0;
@@ -907,11 +1061,14 @@
attachment_views_ = state.GetCurrentAttachmentViews(cb_state);
RecordLayoutTransitions(tag);
+ RecordLoadOperations(cb_state.activeRenderPassBeginInfo.renderArea, tag);
}
-void RenderPassAccessContext::RecordNextSubpass(const ResourceUsageTag &tag) {
+
+void RenderPassAccessContext::RecordNextSubpass(const VkRect2D &render_area, const ResourceUsageTag &tag) {
current_subpass_++;
assert(current_subpass_ < subpass_contexts_.size());
RecordLayoutTransitions(tag);
+ RecordLoadOperations(render_area, tag);
}
void RenderPassAccessContext::RecordEndRenderPass(const ResourceUsageTag &tag) {