syncval: Add QueueSubmit2 support

Refactor the QueueSubmit support to cover all QueueSubmit variations by
conversion to the QueueSubmit2 style parameterization.
diff --git a/layers/synchronization_validation.cpp b/layers/synchronization_validation.cpp
index 7748d57..d9cc346 100644
--- a/layers/synchronization_validation.cpp
+++ b/layers/synchronization_validation.cpp
@@ -3896,6 +3896,32 @@
     return snapshot;
 }
 
+struct QueueSubmitCmdState {
+    std::shared_ptr<const QueueSyncState> queue;
+    std::shared_ptr<QueueBatchContext> last_batch;
+    std::string submit_func_name;
+    AccessLogger logger;
+    SignaledSemaphores signaled;
+    QueueSubmitCmdState(const char *func_name, const AccessLogger &parent_log, const SignaledSemaphores &parent_semaphores)
+        : submit_func_name(func_name), logger(&parent_log), signaled(parent_semaphores) {}
+};
+
+bool QueueBatchContext::DoQueueSubmitValidate(const SyncValidator &sync_state, QueueSubmitCmdState &cmd_state,
+                                              const VkSubmitInfo2 &batch_info) {
+    bool skip = false;
+
+    //  For each submit in the batch...
+    for (const auto &cb : command_buffers_) {
+        if (cb.cb->GetTagLimit() == 0) continue;  // Skip empty CB's
+        skip |= cb.cb->ValidateFirstUse(*this, cmd_state.submit_func_name.c_str(), cb.index);
+
+        // The barriers have already been applied in ValidatFirstUse
+        ResourceUsageRange tag_range = ImportRecordedAccessLog(*cb.cb);
+        ResolveSubmittedCommandBuffer(*cb.cb->GetCurrentAccessContext(), tag_range.begin);
+    }
+    return skip;
+}
+
 bool SignaledSemaphores::SignalSemaphore(const std::shared_ptr<const SEMAPHORE_STATE> &sem_state,
                                          const std::shared_ptr<QueueBatchContext> &batch,
                                          const VkSemaphoreSubmitInfo &signal_info) {
@@ -7237,27 +7263,23 @@
     waitable_fences_.clear();
 }
 
-struct QueueSubmitCmdState {
-    std::shared_ptr<const QueueSyncState> queue;
-    std::shared_ptr<QueueBatchContext> last_batch;
-    AccessLogger logger;
-    SignaledSemaphores signaled;
-    QueueSubmitCmdState(const AccessLogger &parent_log, const SignaledSemaphores &parent_semaphores)
-        : logger(&parent_log), signaled(parent_semaphores) {}
-};
-
 template <>
 thread_local layer_data::optional<QueueSubmitCmdState> layer_data::TlsGuard<QueueSubmitCmdState>::payload_{};
 
 bool SyncValidator::PreCallValidateQueueSubmit(VkQueue queue, uint32_t submitCount, const VkSubmitInfo *pSubmits,
                                                VkFence fence) const {
+    SubmitInfoConverter submit_info(submitCount, pSubmits);
+    return ValidateQueueSubmit(queue, submitCount, submit_info.info2s.data(), fence, "VkQueueSubmit");
+}
+
+bool SyncValidator::ValidateQueueSubmit(VkQueue queue, uint32_t submitCount, const VkSubmitInfo2 *pSubmits, VkFence fence,
+                                        const char *func_name) const {
     bool skip = false;
 
     // Since this early return is above the TlsGuard, the Record phase must also be.
     if (!enabled[sync_validation_queue_submit]) return skip;
 
-    layer_data::TlsGuard<QueueSubmitCmdState> cmd_state(&skip, global_access_log_, signaled_semaphores_);
-    const auto fence_state = Get<FENCE_STATE>(fence);
+    layer_data::TlsGuard<QueueSubmitCmdState> cmd_state(&skip, func_name, global_access_log_, signaled_semaphores_);
     cmd_state->queue = GetQueueSyncStateShared(queue);
     if (!cmd_state->queue) return skip;  // Invalid Queue
 
@@ -7270,32 +7292,24 @@
     std::shared_ptr<const QueueBatchContext> last_batch = cmd_state->queue->LastBatch();
     std::shared_ptr<QueueBatchContext> batch;
     for (uint32_t batch_idx = 0; batch_idx < submitCount; batch_idx++) {
-        const VkSubmitInfo &submit = pSubmits[batch_idx];
+        const VkSubmitInfo2 &submit = pSubmits[batch_idx];
         batch = std::make_shared<QueueBatchContext>(*this, *cmd_state->queue);
-        batch->Setup(last_batch, submit, cmd_state->signaled);
+        batch->SetupCommandBufferInfo(submit);
+        batch->SetupAccessContext(last_batch, submit, cmd_state->signaled);
 
         // Skip import and validation of empty batches
         if (batch->GetTagRange().size()) {
             batch->SetBatchLog(cmd_state->logger, submit_id, batch_idx);
 
-            //  For each submit in the batch...
-            for (const auto &cb : *batch) {
-                if (cb.cb->GetTagLimit() == 0) continue;  // Skip empty CB's
-                skip |= cb.cb->ValidateFirstUse(*batch.get(), "vkQueueSubmit", cb.index);
-
-                // The barriers have already been applied in ValidatFirstUse
-                ResourceUsageRange tag_range = batch->ImportRecordedAccessLog(*cb.cb);
-                batch->ResolveSubmittedCommandBuffer(*cb.cb->GetCurrentAccessContext(), tag_range.begin);
-            }
+            skip |= batch->DoQueueSubmitValidate(*this, *cmd_state, submit);
         }
 
         // Empty batches could have semaphores, though.
-        for (auto &sem : layer_data::make_span(submit.pSignalSemaphores, submit.signalSemaphoreCount)) {
+        for (uint32_t sem_idx = 0; sem_idx < submit.signalSemaphoreInfoCount; ++sem_idx) {
+            const VkSemaphoreSubmitInfo &semaphore_info = submit.pSignalSemaphoreInfos[sem_idx];
             // Make a copy of the state, signal the copy and pend it...
-            auto sem_state = Get<SEMAPHORE_STATE>(sem);
+            auto sem_state = Get<SEMAPHORE_STATE>(semaphore_info.semaphore);
             if (!sem_state) continue;
-            auto semaphore_info = lvl_init_struct<VkSemaphoreSubmitInfo>();
-            semaphore_info.stageMask = VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT;
             cmd_state->signaled.SignalSemaphore(sem_state, batch, semaphore_info);
         }
         // Unless the previous batch was referenced by a signal, the QueueBatchContext will self destruct, but as
@@ -7315,6 +7329,10 @@
                                               VkResult result) {
     StateTracker::PostCallRecordQueueSubmit(queue, submitCount, pSubmits, fence, result);
 
+    RecordQueueSubmit(queue, fence, result);
+}
+
+void SyncValidator::RecordQueueSubmit(VkQueue queue, VkFence fence, VkResult result) {
     // If this return is above the TlsGuard, then the Validate phase return must also be.
     if (!enabled[sync_validation_queue_submit]) return;  // Queue submit validation must be affirmatively enabled
 
@@ -7366,21 +7384,22 @@
 
 bool SyncValidator::PreCallValidateQueueSubmit2KHR(VkQueue queue, uint32_t submitCount, const VkSubmitInfo2KHR *pSubmits,
                                                    VkFence fence) const {
-    bool skip = false;
-    if (!enabled[sync_validation_queue_submit]) return skip;
-
-    // WIP: Add Submit2 support
-    return skip;
+    return ValidateQueueSubmit(queue, submitCount, pSubmits, fence, "VkQueueSubmit2KHR");
+}
+bool SyncValidator::PreCallValidateQueueSubmit2(VkQueue queue, uint32_t submitCount, const VkSubmitInfo2KHR *pSubmits,
+                                                VkFence fence) const {
+    return ValidateQueueSubmit(queue, submitCount, pSubmits, fence, "VkQueueSubmit2");
 }
 
 void SyncValidator::PostCallRecordQueueSubmit2KHR(VkQueue queue, uint32_t submitCount, const VkSubmitInfo2KHR *pSubmits,
                                                   VkFence fence, VkResult result) {
     StateTracker::PostCallRecordQueueSubmit2KHR(queue, submitCount, pSubmits, fence, result);
-    if (VK_SUCCESS != result) return;  // dispatched QueueSubmit2 failed
-
-    if (!enabled[sync_validation_queue_submit]) return;
-
-    // WIP: Add Submit2 support
+    RecordQueueSubmit(queue, fence, result);
+}
+void SyncValidator::PostCallRecordQueueSubmit2(VkQueue queue, uint32_t submitCount, const VkSubmitInfo2KHR *pSubmits, VkFence fence,
+                                               VkResult result) {
+    StateTracker::PostCallRecordQueueSubmit2(queue, submitCount, pSubmits, fence, result);
+    RecordQueueSubmit(queue, fence, result);
 }
 
 void SyncValidator::PostCallRecordGetFenceStatus(VkDevice device, VkFence fence, VkResult result) {
@@ -7515,12 +7534,6 @@
       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,
-                              SignaledSemaphores &signaled) {
-    SetupCommandBufferInfo(batch_info);
-    SetupAccessContext(prev_batch, batch_info, signaled);
-}
 void QueueBatchContext::ResolveSubmittedCommandBuffer(const AccessContext &recorded_context, ResourceUsageTag offset) {
     GetCurrentAccessContext()->ResolveFromContext(QueueTagOffsetBarrierAction(GetQueueId(), offset), recorded_context);
 }
@@ -7637,32 +7650,7 @@
     return signal_state->batch;
 }
 
-// Accessor Traits to allow Submit and Submit2 constructors to call the same utilities
-template <>
-class QueueBatchContext::SubmitInfoAccessor<VkSubmitInfo> {
-  public:
-    SubmitInfoAccessor(const VkSubmitInfo &info) : info_(info) {}
-    inline uint32_t WaitSemaphoreCount() const { return info_.waitSemaphoreCount; }
-    inline VkSemaphore WaitSemaphore(uint32_t index) { return info_.pWaitSemaphores[index]; }
-    inline VkPipelineStageFlags2 WaitDstMask(uint32_t index) { return info_.pWaitDstStageMask[index]; }
-    inline uint32_t CommandBufferCount() const { return info_.commandBufferCount; }
-    inline VkCommandBuffer CommandBuffer(uint32_t index) { return info_.pCommandBuffers[index]; }
-
-  private:
-    const VkSubmitInfo &info_;
-};
-template <typename BatchInfo, typename Fn>
-void QueueBatchContext::ForEachWaitSemaphore(const BatchInfo &batch_info, Fn &&func) {
-    using Accessor = QueueBatchContext::SubmitInfoAccessor<BatchInfo>;
-    Accessor batch(batch_info);
-    const uint32_t wait_count = batch.WaitSemaphoreCount();
-    for (uint32_t i = 0; i < wait_count; ++i) {
-        func(batch.WaitSemaphore(i), batch.WaitDstMask(i));
-    }
-}
-
-template <typename BatchInfo>
-void QueueBatchContext::SetupAccessContext(const std::shared_ptr<const QueueBatchContext> &prev, const BatchInfo &batch_info,
+void QueueBatchContext::SetupAccessContext(const std::shared_ptr<const QueueBatchContext> &prev, const VkSubmitInfo2 &submit_info,
                                            SignaledSemaphores &signaled) {
     // Copy in the event state from the previous batch (on this queue)
     if (prev) {
@@ -7671,12 +7659,14 @@
 
     // Import (resolve) the batches that are waited on, with the semaphore's effective barriers applied
     layer_data::unordered_set<std::shared_ptr<const QueueBatchContext>> batches_resolved;
-    ForEachWaitSemaphore(batch_info, [this, &signaled, &batches_resolved](VkSemaphore sem, VkPipelineStageFlags2 wait_mask) {
-        std::shared_ptr<QueueBatchContext> resolved = ResolveOneWaitSemaphore(sem, wait_mask, signaled);
+    const uint32_t wait_count = submit_info.waitSemaphoreInfoCount;
+    const VkSemaphoreSubmitInfo *wait_infos = submit_info.pWaitSemaphoreInfos;
+    for (const auto &wait_info : layer_data::make_span(wait_infos, wait_count)) {
+        std::shared_ptr<QueueBatchContext> resolved = ResolveOneWaitSemaphore(wait_info.semaphore, wait_info.stageMask, signaled);
         if (resolved) {
             batches_resolved.emplace(std::move(resolved));
         }
-    });
+    }
 
     // If there are no semaphores to the previous batch, make sure a "submit order" non-barriered import is done
     if (prev && !layer_data::Contains(batches_resolved, prev)) {
@@ -7693,19 +7683,17 @@
     }
 }
 
-template <typename BatchInfo>
-void QueueBatchContext::SetupCommandBufferInfo(const BatchInfo &batch_info) {
-    using Accessor = QueueBatchContext::SubmitInfoAccessor<BatchInfo>;
-    Accessor batch(batch_info);
-
+void QueueBatchContext::SetupCommandBufferInfo(const VkSubmitInfo2 &submit_info) {
     // Create the list of command buffers to submit
-    const uint32_t cb_count = batch.CommandBufferCount();
+    const uint32_t cb_count = submit_info.commandBufferInfoCount;
+    const VkCommandBufferSubmitInfo *const cb_infos = submit_info.pCommandBufferInfos;
     command_buffers_.reserve(cb_count);
-    for (uint32_t index = 0; index < cb_count; ++index) {
-        auto cb_context = sync_state_->GetAccessContextShared(batch.CommandBuffer(index));
+
+    for (const auto &cb_info : layer_data::make_span(cb_infos, cb_count)) {
+        auto cb_context = sync_state_->GetAccessContextShared(cb_info.commandBuffer);
         if (cb_context) {
             tag_range_.end += cb_context->GetTagLimit();
-            command_buffers_.emplace_back(index, std::move(cb_context));
+            command_buffers_.emplace_back(static_cast<uint32_t>(&cb_info - cb_infos), std::move(cb_context));
         }
     }
 }
@@ -7841,3 +7829,56 @@
 }
 
 FenceSyncState::FenceSyncState() : fence(), tag(kInvalidTag), queue_id(QueueSyncState::kQueueIdInvalid) {}
+
+VkSemaphoreSubmitInfo SubmitInfoConverter::BatchStore::WaitSemaphore(const VkSubmitInfo &info, uint32_t index) {
+    auto semaphore_info = lvl_init_struct<VkSemaphoreSubmitInfo>();
+    semaphore_info.semaphore = info.pWaitSemaphores[index];
+    semaphore_info.stageMask = info.pWaitDstStageMask[index];
+    return semaphore_info;
+}
+VkCommandBufferSubmitInfo SubmitInfoConverter::BatchStore::CommandBuffer(const VkSubmitInfo &info, uint32_t index) {
+    auto cb_info = lvl_init_struct<VkCommandBufferSubmitInfo>();
+    cb_info.commandBuffer = info.pCommandBuffers[index];
+    return cb_info;
+}
+
+VkSemaphoreSubmitInfo SubmitInfoConverter::BatchStore::SignalSemaphore(const VkSubmitInfo &info, uint32_t index) {
+    auto semaphore_info = lvl_init_struct<VkSemaphoreSubmitInfo>();
+    semaphore_info.semaphore = info.pSignalSemaphores[index];
+    semaphore_info.stageMask = VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT;
+    return semaphore_info;
+}
+
+SubmitInfoConverter::BatchStore::BatchStore(const VkSubmitInfo &info) {
+    info2 = lvl_init_struct<VkSubmitInfo2>();
+
+    info2.waitSemaphoreInfoCount = info.waitSemaphoreCount;
+    waits.reserve(info2.waitSemaphoreInfoCount);
+    for (uint32_t i = 0; i < info2.waitSemaphoreInfoCount; ++i) {
+        waits.emplace_back(WaitSemaphore(info, i));
+    }
+    info2.pWaitSemaphoreInfos = waits.data();
+
+    info2.commandBufferInfoCount = info.commandBufferCount;
+    cbs.reserve(info2.commandBufferInfoCount);
+    for (uint32_t i = 0; i < info2.commandBufferInfoCount; ++i) {
+        cbs.emplace_back(CommandBuffer(info, i));
+    }
+    info2.pCommandBufferInfos = cbs.data();
+
+    info2.signalSemaphoreInfoCount = info.signalSemaphoreCount;
+    signals.reserve(info2.signalSemaphoreInfoCount);
+    for (uint32_t i = 0; i < info2.signalSemaphoreInfoCount; ++i) {
+        signals.emplace_back(SignalSemaphore(info, i));
+    }
+    info2.pSignalSemaphoreInfos = signals.data();
+}
+
+SubmitInfoConverter::SubmitInfoConverter(uint32_t count, const VkSubmitInfo *infos) {
+    info_store.reserve(count);
+    info2s.reserve(count);
+    for (uint32_t batch = 0; batch < count; ++batch) {
+        info_store.emplace_back(infos[batch]);
+        info2s.emplace_back(info_store.back().info2);
+    }
+}