syncval: Correct replay for render pass ops

Rework the syncops replay code for render pass operations.
diff --git a/layers/synchronization_validation.cpp b/layers/synchronization_validation.cpp
index da6599a..2df8b20 100644
--- a/layers/synchronization_validation.cpp
+++ b/layers/synchronization_validation.cpp
@@ -269,6 +269,17 @@
     const bool layout_transition = false;
 };
 
+static void InitSubpassContexts(VkQueueFlags queue_flags, const RENDER_PASS_STATE &rp_state, const AccessContext *external_context,
+                                std::vector<AccessContext> &subpass_contexts) {
+    const auto &create_info = rp_state.createInfo;
+    // Add this for all subpasses here so that they exsist during next subpass validation
+    subpass_contexts.clear();
+    subpass_contexts.reserve(create_info.subpassCount);
+    for (uint32_t pass = 0; pass < create_info.subpassCount; pass++) {
+        subpass_contexts.emplace_back(pass, queue_flags, rp_state.subpass_dependencies, subpass_contexts, external_context);
+    }
+}
+
 // NOTE: Make sure the proxy doesn't outlive from, as the proxy is pointing directly to access contexts owned by from.
 CommandBufferAccessContext::CommandBufferAccessContext(const CommandBufferAccessContext &from, AsProxyContext dummy)
     : CommandBufferAccessContext(from.sync_state_) {
@@ -326,6 +337,7 @@
     return out.str();
 }
 
+
 bool CommandExecutionContext::ValidForSyncOps() const {
     bool valid = GetCurrentEventsContext() && GetCurrentAccessContext();
     assert(valid);
@@ -2364,6 +2376,8 @@
     const AccessContext *recorded_context = GetCurrentAccessContext();
     assert(recorded_context);
     HazardResult hazard;
+    ReplayGuard replay_guard(exec_context, *this);
+
     auto log_msg = [this](const HazardResult &hazard, const CommandExecutionContext &exec_context, const char *func_name,
                           uint32_t index) {
         const auto handle = exec_context.Handle();
@@ -2382,7 +2396,7 @@
 
         // We're allowing for the ReplayRecord to modify the exec_context (e.g. for Renderpass operations), so
         // we need to fetch the current access context each time
-        hazard = recorded_context->DetectFirstUseHazard(queue_id, tag_range, *exec_context.GetCurrentAccessContext());
+        hazard = exec_context.DetectFirstUseHazard(tag_range);
         if (hazard.hazard) {
             skip |= log_msg(hazard, exec_context, func_name, index);
         }
@@ -2424,6 +2438,10 @@
     GetCurrentAccessContext()->ResolveFromContext(tag_offset, recorded_context);
 }
 
+HazardResult CommandBufferAccessContext::DetectFirstUseHazard(const ResourceUsageRange &tag_range) {
+    return current_replay_->GetCurrentAccessContext()->DetectFirstUseHazard(GetQueueId(), tag_range, *GetCurrentAccessContext());
+}
+
 ResourceUsageRange CommandExecutionContext::ImportRecordedAccessLog(const CommandBufferAccessContext &recorded_context) {
     // The execution references ensure lifespan for the referenced child CB's...
     ResourceUsageRange tag_range(GetTagLimit(), 0);
@@ -2817,11 +2835,8 @@
                                                  const std::vector<const IMAGE_VIEW_STATE *> &attachment_views,
                                                  const AccessContext *external_context)
     : rp_state_(&rp_state), render_area_(render_area), current_subpass_(0U), attachment_views_() {
-    // Add this for all subpasses here so that they exsist during next subpass validation
-    subpass_contexts_.reserve(rp_state_->createInfo.subpassCount);
-    for (uint32_t pass = 0; pass < rp_state_->createInfo.subpassCount; pass++) {
-        subpass_contexts_.emplace_back(pass, queue_flags, rp_state_->subpass_dependencies, subpass_contexts_, external_context);
-    }
+    // Add this for all subpasses here so that they exist during next subpass validation
+    InitSubpassContexts(queue_flags, rp_state, external_context, subpass_contexts_);
     attachment_views_ = CreateAttachmentViewGen(render_area, attachment_views);
 }
 void RenderPassAccessContext::RecordBeginRenderPass(const ResourceUsageTag barrier_tag, const ResourceUsageTag load_tag) {
@@ -6206,7 +6221,7 @@
     }
 }
 
-ResourceUsageTag SyncOpPipelineBarrier::Record(CommandBufferAccessContext *cb_context) const {
+ResourceUsageTag SyncOpPipelineBarrier::Record(CommandBufferAccessContext *cb_context) {
     const auto tag = cb_context->NextCommandTag(cmd_type_);
     ReplayRecord(*cb_context, tag);
     return tag;
@@ -6602,7 +6617,7 @@
     SyncEventState *sync_event;
 };
 
-ResourceUsageTag SyncOpWaitEvents::Record(CommandBufferAccessContext *cb_context) const {
+ResourceUsageTag SyncOpWaitEvents::Record(CommandBufferAccessContext *cb_context) {
     const auto tag = cb_context->NextCommandTag(cmd_type_);
 
     ReplayRecord(*cb_context, tag);
@@ -6752,7 +6767,7 @@
     return skip;
 }
 
-ResourceUsageTag SyncOpResetEvent::Record(CommandBufferAccessContext *cb_context) const {
+ResourceUsageTag SyncOpResetEvent::Record(CommandBufferAccessContext *cb_context) {
     const auto tag = cb_context->NextCommandTag(cmd_type_);
     ReplayRecord(*cb_context, tag);
     return tag;
@@ -6876,7 +6891,7 @@
     return skip;
 }
 
-ResourceUsageTag SyncOpSetEvent::Record(CommandBufferAccessContext *cb_context) const {
+ResourceUsageTag SyncOpSetEvent::Record(CommandBufferAccessContext *cb_context) {
     const auto tag = cb_context->NextCommandTag(cmd_type_);
     auto *events_context = cb_context->GetCurrentEventsContext();
     const QueueId queue_id = cb_context->GetQueueId();
@@ -6935,7 +6950,7 @@
 SyncOpBeginRenderPass::SyncOpBeginRenderPass(CMD_TYPE cmd_type, const SyncValidator &sync_state,
                                              const VkRenderPassBeginInfo *pRenderPassBegin,
                                              const VkSubpassBeginInfo *pSubpassBeginInfo)
-    : SyncOpBase(cmd_type) {
+    : SyncOpBase(cmd_type), rp_context_(nullptr) {
     if (pRenderPassBegin) {
         rp_state_ = sync_state.Get<RENDER_PASS_STATE>(pRenderPassBegin->renderPass);
         renderpass_begin_info_ = safe_VkRenderPassBeginInfo(pRenderPassBegin);
@@ -6991,11 +7006,16 @@
     return skip;
 }
 
-ResourceUsageTag SyncOpBeginRenderPass::Record(CommandBufferAccessContext *cb_context) const {
-    // TODO PHASE2 need to have a consistent way to record to either command buffer or queue contexts
+ResourceUsageTag SyncOpBeginRenderPass::Record(CommandBufferAccessContext *cb_context) {
     assert(rp_state_.get());
     if (nullptr == rp_state_.get()) return cb_context->NextCommandTag(cmd_type_);
-    return cb_context->RecordBeginRenderPass(cmd_type_, *rp_state_.get(), renderpass_begin_info_.renderArea, attachments_);
+    const ResourceUsageTag begin_tag =
+        cb_context->RecordBeginRenderPass(cmd_type_, *rp_state_.get(), renderpass_begin_info_.renderArea, attachments_);
+
+    // Note: this state update must be after RecordBeginRenderPass as there is no current render pass until that function runs
+    rp_context_ = cb_context->GetCurrentRenderPassContext();
+
+    return begin_tag;
 }
 
 bool SyncOpBeginRenderPass::ReplayValidate(ResourceUsageTag recorded_tag, const CommandBufferAccessContext &recorded_context,
@@ -7003,7 +7023,15 @@
     return false;
 }
 
-void SyncOpBeginRenderPass::ReplayRecord(CommandExecutionContext &exec_context, ResourceUsageTag tag) const {}
+void SyncOpBeginRenderPass::ReplayRecord(CommandExecutionContext &exec_context, ResourceUsageTag tag) const {
+    // Need to update the exec_contexts state (which for RenderPass operations *must* be a QueueBatchContext, as
+    // render pass operations are not allowed in secondary command buffers.
+    const QueueId queue_id = exec_context.GetQueueId();
+    assert(queue_id != QueueSyncState::kQueueIdInvalid);  // Renderpass replay only valid at submit (not exec) time
+    if (queue_id == QueueSyncState::kQueueIdInvalid) return;
+
+    exec_context.BeginRenderPassReplay(*this, tag);
+}
 
 SyncOpNextSubpass::SyncOpNextSubpass(CMD_TYPE cmd_type, const SyncValidator &sync_state,
                                      const VkSubpassBeginInfo *pSubpassBeginInfo, const VkSubpassEndInfo *pSubpassEndInfo)
@@ -7025,7 +7053,7 @@
     return skip;
 }
 
-ResourceUsageTag SyncOpNextSubpass::Record(CommandBufferAccessContext *cb_context) const {
+ResourceUsageTag SyncOpNextSubpass::Record(CommandBufferAccessContext *cb_context) {
     return cb_context->RecordNextSubpass(cmd_type_);
 }
 
@@ -7042,7 +7070,9 @@
     }
 }
 
-void SyncOpNextSubpass::ReplayRecord(CommandExecutionContext &exec_context, ResourceUsageTag tag) const {}
+void SyncOpNextSubpass::ReplayRecord(CommandExecutionContext &exec_context, ResourceUsageTag tag) const {
+    exec_context.NextSubpassReplay();
+}
 
 bool SyncOpEndRenderPass::Validate(const CommandBufferAccessContext &cb_context) const {
     bool skip = false;
@@ -7053,7 +7083,7 @@
     return skip;
 }
 
-ResourceUsageTag SyncOpEndRenderPass::Record(CommandBufferAccessContext *cb_context) const {
+ResourceUsageTag SyncOpEndRenderPass::Record(CommandBufferAccessContext *cb_context) {
     return cb_context->RecordEndRenderPass(cmd_type_);
 }
 
@@ -7062,7 +7092,9 @@
     return false;
 }
 
-void SyncOpEndRenderPass::ReplayRecord(CommandExecutionContext &exec_context, ResourceUsageTag tag) const {}
+void SyncOpEndRenderPass::ReplayRecord(CommandExecutionContext &exec_context, ResourceUsageTag tag) const {
+    exec_context.EndRenderPassReplay();
+}
 
 void SyncValidator::PreCallRecordCmdWriteBufferMarker2AMD(VkCommandBuffer commandBuffer, VkPipelineStageFlags2KHR pipelineStage,
                                                           VkBuffer dstBuffer, VkDeviceSize dstOffset, uint32_t marker) {
@@ -7404,7 +7436,11 @@
 }
 
 QueueBatchContext::QueueBatchContext(const SyncValidator &sync_state, const QueueSyncState &queue_state)
-    : CommandExecutionContext(&sync_state), queue_state_(&queue_state), tag_range_(0, 0), batch_log_(nullptr) {}
+    : CommandExecutionContext(&sync_state),
+      queue_state_(&queue_state),
+      tag_range_(0, 0),
+      current_access_context_(&access_context_),
+      batch_log_(nullptr) {}
 
 template <typename BatchInfo>
 void QueueBatchContext::Setup(const std::shared_ptr<const QueueBatchContext> &prev_batch, const BatchInfo &batch_info,
@@ -7441,6 +7477,58 @@
     events_context_.ApplyTaggedWait(GetQueueFlags(), ResourceUsageRecord::kMaxIndex);
 }
 
+HazardResult QueueBatchContext::DetectFirstUseHazard(const ResourceUsageRange &tag_range) {
+    // Queue batch handling requires dealing with renderpass state and picking the correct access context
+    if (rp_replay_) {
+        return rp_replay_.replay_context->DetectFirstUseHazard(GetQueueId(), tag_range, *current_access_context_);
+    }
+    return current_replay_->GetCurrentAccessContext()->DetectFirstUseHazard(GetQueueId(), tag_range, access_context_);
+}
+
+void QueueBatchContext::BeginRenderPassReplay(const SyncOpBeginRenderPass &begin_op, const ResourceUsageTag tag) {
+    current_access_context_ = rp_replay_.Begin(GetQueueFlags(), begin_op, access_context_);
+    current_access_context_->ResolvePreviousAccesses();
+}
+
+void QueueBatchContext::NextSubpassReplay() {
+    current_access_context_ = rp_replay_.Next();
+    current_access_context_->ResolvePreviousAccesses();
+}
+
+void QueueBatchContext::EndRenderPassReplay() {
+    rp_replay_.End(access_context_);
+    current_access_context_ = &access_context_;
+}
+
+AccessContext *QueueBatchContext::RenderPassReplayState::Begin(VkQueueFlags queue_flags, const SyncOpBeginRenderPass &begin_op_,
+                                                               const AccessContext &external_context) {
+    Reset();
+
+    begin_op = &begin_op_;
+    subpass = 0;
+
+    const RenderPassAccessContext *rp_context = begin_op->GetRenderPassAccessContext();
+    assert(rp_context);
+    replay_context = &rp_context->GetContexts()[0];
+
+    InitSubpassContexts(queue_flags, *rp_context->GetRenderPassState(), &external_context, subpass_contexts);
+    return &subpass_contexts[0];
+}
+
+AccessContext *QueueBatchContext::RenderPassReplayState::Next() {
+    subpass++;
+
+    const RenderPassAccessContext *rp_context = begin_op->GetRenderPassAccessContext();
+
+    replay_context = &rp_context->GetContexts()[subpass];
+    return &subpass_contexts[subpass];
+}
+
+void QueueBatchContext::RenderPassReplayState::End(AccessContext &external_context) {
+    external_context.ResolveChildContexts(subpass_contexts);
+    Reset();
+}
+
 class ApplySemaphoreBarrierAction {
   public:
     ApplySemaphoreBarrierAction(const SemaphoreScope &signal, const SemaphoreScope &wait) : signal_(signal), wait_(wait) {}