<commit_before>/******************************************************************************
 * The MIT License (MIT)
 *
 * Copyright (c) 2015-2018 <NAME>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 ******************************************************************************/

#include "../vk_core.h"

template <typename SerialiserType>
bool WrappedVulkan::Serialise_vkGetDeviceQueue(SerialiserType &ser, VkDevice device,
                                               uint32_t queueFamilyIndex, uint32_t queueIndex,
                                               VkQueue *pQueue)
{
  SERIALISE_ELEMENT(device);
  SERIALISE_ELEMENT(queueFamilyIndex);
  SERIALISE_ELEMENT(queueIndex);
  SERIALISE_ELEMENT_LOCAL(Queue, GetResID(*pQueue)).TypedAs("VkQueue");

  SERIALISE_CHECK_READ_ERRORS();

  if(IsReplayingAndReading())
  {
    VkQueue queue;

    uint32_t remapFamily = m_QueueRemapping[queueFamilyIndex][queueIndex].family;
    uint32_t remapIndex = m_QueueRemapping[queueFamilyIndex][queueIndex].index;

    if(remapFamily != queueFamilyIndex || remapIndex != queueIndex)
      RDCLOG("Remapped Queue %u/%u from capture to %u/%u on replay", queueFamilyIndex, queueIndex,
             remapFamily, remapIndex);

    ObjDisp(device)->GetDeviceQueue(Unwrap(device), remapFamily, remapIndex, &queue);

    GetResourceManager()->WrapResource(Unwrap(device), queue);
    GetResourceManager()->AddLiveResource(Queue, queue);

    if(remapFamily == m_QueueFamilyIdx && m_Queue == VK_NULL_HANDLE)
    {
      m_Queue = queue;

      // we can now submit any cmds that were queued (e.g. from creating debug
      // manager on vkCreateDevice)
      SubmitCmds();
    }

    if(remapFamily < m_ExternalQueues.size())
    {
      if(m_ExternalQueues[remapFamily].queue == VK_NULL_HANDLE)
        m_ExternalQueues[remapFamily].queue = queue;
    }
    else
    {
      RDCERR("Unexpected queue family index %u", remapFamily);
    }

    m_CreationInfo.m_Queue[GetResID(queue)] = remapFamily;

    AddResource(Queue, ResourceType::Queue, "Queue");
    DerivedResource(device, Queue);
  }

  return true;
}

void WrappedVulkan::vkGetDeviceQueue(VkDevice device, uint32_t queueFamilyIndex,
                                     uint32_t queueIndex, VkQueue *pQueue)
{
  SERIALISE_TIME_CALL(
      ObjDisp(device)->GetDeviceQueue(Unwrap(device), queueFamilyIndex, queueIndex, pQueue));

  if(m_SetDeviceLoaderData)
    m_SetDeviceLoaderData(m_Device, *pQueue);
  else
    SetDispatchTableOverMagicNumber(device, *pQueue);

  RDCASSERT(IsCaptureMode(m_State));

  {
    // it's perfectly valid for enumerate type functions to return the same handle
    // each time. If that happens, we will already have a wrapper created so just
    // return the wrapped object to the user and do nothing else
    if(m_QueueFamilies[queueFamilyIndex][queueIndex] != VK_NULL_HANDLE)
    {
      *pQueue = m_QueueFamilies[queueFamilyIndex][queueIndex];
    }
    else
    {
      ResourceId id = GetResourceManager()->WrapResource(Unwrap(device), *pQueue);

      {
        Chunk *chunk = NULL;

        {
          CACHE_THREAD_SERIALISER();

          SCOPED_SERIALISE_CHUNK(VulkanChunk::vkGetDeviceQueue);
          Serialise_vkGetDeviceQueue(ser, device, queueFamilyIndex, queueIndex, pQueue);

          chunk = scope.Get();
        }

        VkResourceRecord *record = GetResourceManager()->AddResourceRecord(*pQueue);
        RDCASSERT(record);

        record->queueFamilyIndex = queueFamilyIndex;

        VkResourceRecord *instrecord = GetRecord(m_Instance);

        // treat queues as pool members of the instance (ie. freed when the instance dies)
        {
          instrecord->LockChunks();
          instrecord->pooledChildren.push_back(record);
          instrecord->UnlockChunks();
        }

        record->AddChunk(chunk);
      }

      m_QueueFamilies[queueFamilyIndex][queueIndex] = *pQueue;

      if(queueFamilyIndex < m_ExternalQueues.size())
      {
        if(m_ExternalQueues[queueFamilyIndex].queue == VK_NULL_HANDLE)
          m_ExternalQueues[queueFamilyIndex].queue = *pQueue;
      }
      else
      {
        RDCERR("Unexpected queue family index %u", queueFamilyIndex);
      }

      if(queueFamilyIndex == m_QueueFamilyIdx)
      {
        m_Queue = *pQueue;

        // we can now submit any cmds that were queued (e.g. from creating debug
        // manager on vkCreateDevice)
        SubmitCmds();
      }
    }
  }
}

template <typename SerialiserType>
bool WrappedVulkan::Serialise_vkQueueSubmit(SerialiserType &ser, VkQueue queue, uint32_t submitCount,
                                            const VkSubmitInfo *pSubmits, VkFence fence)
{
  SERIALISE_ELEMENT(queue);
  SERIALISE_ELEMENT(submitCount);
  SERIALISE_ELEMENT_ARRAY(pSubmits, submitCount);
  SERIALISE_ELEMENT(fence);

  Serialise_DebugMessages(ser);

  SERIALISE_CHECK_READ_ERRORS();

  if(IsReplayingAndReading())
  {
    // if there are multiple queue submissions in flight, wait for the previous queue to finish
    // before executing this, as we don't have the sync information to properly sync.
    if(m_PrevQueue != queue)
    {
      RDCDEBUG("Previous queue execution was on queue %llu, now executing %llu, syncing GPU",
               GetResID(m_PrevQueue), GetResID(queue));
      if(m_PrevQueue != VK_NULL_HANDLE)
        ObjDisp(m_PrevQueue)->QueueWaitIdle(Unwrap(m_PrevQueue));

      m_PrevQueue = queue;
    }

    // if we ever waited on any semaphores, wait for idle here.
    bool doWait = false;
    for(uint32_t i = 0; i < submitCount; i++)
      if(pSubmits[i].waitSemaphoreCount > 0)
        doWait = true;

    if(doWait)
      ObjDisp(queue)->QueueWaitIdle(Unwrap(queue));

    // add a drawcall use for this submission, to tally up with any debug messages that come from it
    if(IsLoading(m_State))
    {
      DrawcallUse use(m_CurChunkOffset, m_RootEventID);

      // insert in sorted location
      auto drawit = std::lower_bound(m_DrawcallUses.begin(), m_DrawcallUses.end(), use);
      m_DrawcallUses.insert(drawit, use);
    }

    for(uint32_t sub = 0; sub < submitCount; sub++)
    {
      VkSubmitInfo submitInfo = pSubmits[sub];
      submitInfo.pWaitSemaphores = NULL;
      submitInfo.waitSemaphoreCount = 0;
      submitInfo.pSignalSemaphores = NULL;
      submitInfo.signalSemaphoreCount = 0;

      if(IsLoading(m_State))
      {
        // don't submit the fence, since we have nothing to wait on it being signalled, and we might
        // not have it correctly in the unsignalled state.
        VkSubmitInfo unwrapped = submitInfo;

        size_t tempMemSize = unwrapped.commandBufferCount * sizeof(VkCommandBuffer) +
                             GetNextPatchSize(unwrapped.pNext);

        byte *tempMem = GetTempMemory(tempMemSize);

        VkCommandBuffer *unwrappedCmds = (VkCommandBuffer *)tempMem;
        unwrapped.pCommandBuffers = unwrappedCmds;
        for(uint32_t i = 0; i < unwrapped.commandBufferCount; i++)
          unwrappedCmds[i] = Unwrap(submitInfo.pCommandBuffers[i]);

        tempMem += unwrapped.commandBufferCount * sizeof(VkCommandBuffer);

        UnwrapNextChain(m_State, "VkSubmitInfo", tempMem, (VkGenericStruct *)&unwrapped);

        ObjDisp(queue)->QueueSubmit(Unwrap(queue), 1, &unwrapped, VK_NULL_HANDLE);

        AddEvent();

        // we're adding multiple events, need to increment ourselves
        m_RootEventID++;

        std::string basename = StringFormat::Fmt("vkQueueSubmit(%u)", submitInfo.commandBufferCount);

        for(uint32_t c = 0; c < submitInfo.commandBufferCount; c++)
        {
          ResourceId cmd =
              GetResourceManager()->GetOriginalID(GetResID(submitInfo.pCommandBuffers[c]));

          BakedCmdBufferInfo &cmdBufInfo = m_BakedCmdBufferInfo[cmd];

          GetResourceManager()->ApplyBarriers(m_CreationInfo.m_Queue[GetResID(queue)],
                                              m_BakedCmdBufferInfo[cmd].imgbarriers, m_ImageLayouts);

          std::string name = StringFormat::Fmt("=> %s[%u]: vkBeginCommandBuffer(%s)",
                                               basename.c_str(), c, ToStr(cmd).c_str());

          // add a fake marker
          DrawcallDescription draw;
          draw.name = name;
          draw.flags |= DrawFlags::SetMarker;
          AddEvent();

          m_RootEvents.back().chunkIndex = cmdBufInfo.beginChunk;
          m_Events.back().chunkIndex = cmdBufInfo.beginChunk;

          AddDrawcall(draw, true);
          m_RootEventID++;

          // insert the baked command buffer in-line into this list of notes, assigning new event
          // and drawIDs
          InsertDrawsAndRefreshIDs(cmdBufInfo.draw->children);

          for(size_t e = 0; e < cmdBufInfo.draw->executedCmds.size(); e++)
          {
            std::vector<Submission> &submits =
                m_Partial[Secondary].cmdBufferSubmits[cmdBufInfo.draw->executedCmds[e]];

            for(size_t s = 0; s < submits.size(); s++)
            {
              if(!submits[s].rebased)
              {
                submits[s].baseEvent += m_RootEventID;
                submits[s].rebased = true;
              }
            }
          }

          for(size_t i = 0; i < cmdBufInfo.debugMessages.size(); i++)
          {
            m_DebugMessages.push_back(cmdBufInfo.debugMessages[i]);
            m_DebugMessages.back().eventId += m_RootEventID;
          }

          // only primary command buffers can be submitted
          m_Partial[Primary].cmdBufferSubmits[cmd].push_back(Submission(m_RootEventID));

          m_RootEventID += cmdBufInfo.eventCount;
          m_RootDrawcallID += cmdBufInfo.drawCount;

          name = StringFormat::Fmt("=> %s[%u]: vkEndCommandBuffer(%s)", basename.c_str(), c,
                                   ToStr(cmd).c_str());
          draw.name = name;
          AddEvent();

          m_RootEvents.back().chunkIndex = cmdBufInfo.endChunk;
          m_Events.back().chunkIndex = cmdBufInfo.endChunk;

          AddDrawcall(draw, true);
          m_RootEventID++;
        }

        // account for the outer loop thinking we've added one event and incrementing,
        // since we've done all the handling ourselves this will be off by one.
        m_RootEventID--;
      }
      else
      {
        // account for the queue submit event
        m_RootEventID++;

        uint32_t startEID = m_RootEventID;

        // advance m_CurEventID to match the events added when reading
        for(uint32_t c = 0; c < submitInfo.commandBufferCount; c++)
        {
          ResourceId cmd =
              GetResourceManager()->GetOriginalID(GetResID(submitInfo.pCommandBuffers[c]));

          // 2 extra for the virtual labels around the command buffer
          m_RootEventID += 2 + m_BakedCmdBufferInfo[cmd].eventCount;
          m_RootDrawcallID += 2 + m_BakedCmdBufferInfo[cmd].drawCount;
        }

        // same accounting for the outer loop as above
        m_RootEventID--;

        if(submitInfo.commandBufferCount == 0)
        {
          // do nothing, don't bother with the logic below
        }
        else if(m_LastEventID <= startEID)
        {
#if ENABLED(VERBOSE_PARTIAL_REPLAY)
          RDCDEBUG("Queue Submit no replay %u == %u", m_LastEventID, startEID);
#endif
        }
        else
        {
#if ENABLED(VERBOSE_PARTIAL_REPLAY)
          RDCDEBUG("Queue Submit from re-recorded commands, root EID %u last EID", m_RootEventID,
                   m_LastEventID);
#endif

          uint32_t eid = startEID;

          std::vector<VkCommandBuffer> rerecordedCmds;

          for(uint32_t c = 0; c < submitInfo.commandBufferCount; c++)
          {
            ResourceId cmdId =
                GetResourceManager()->GetOriginalID(GetResID(submitInfo.pCommandBuffers[c]));

            // account for the virtual vkBeginCommandBuffer label at the start of the events here
            // so it matches up to baseEvent
            eid++;

#if ENABLED(VERBOSE_PARTIAL_REPLAY)
            uint32_t end = eid + m_BakedCmdBufferInfo[cmdId].eventCount;
#endif

            if(eid <= m_LastEventID)
            {
              VkCommandBuffer cmd = RerecordCmdBuf(cmdId);
              ResourceId rerecord = GetResID(cmd);
#if ENABLED(VERBOSE_PARTIAL_REPLAY)
              RDCDEBUG("Queue Submit re-recorded replay of %llu, using %llu (%u -> %u <= %u)",
                       cmdId, rerecord, eid, end, m_LastEventID);
#endif
              rerecordedCmds.push_back(Unwrap(cmd));

              GetResourceManager()->ApplyBarriers(m_CreationInfo.m_Queue[GetResID(queue)],
                                                  m_BakedCmdBufferInfo[rerecord].imgbarriers,
                                                  m_ImageLayouts);
            }
            else
            {
#if ENABLED(VERBOSE_PARTIAL_REPLAY)
              RDCDEBUG("Queue not submitting %llu", cmdId);
#endif
            }

            // 1 extra to account for the virtual end command buffer label (begin is accounted for
            // above)
            eid += 1 + m_BakedCmdBufferInfo[cmdId].eventCount;
          }

          VkSubmitInfo rerecordedSubmit = submitInfo;

          rerecordedSubmit.commandBufferCount = (uint32_t)rerecordedCmds.size();
          rerecordedSubmit.pCommandBuffers = &rerecordedCmds[0];

#if ENABLED(SINGLE_FLUSH_VALIDATE)
          rerecordedSubmit.commandBufferCount = 1;
          for(uint32_t i = 0; i < rerecordedSubmit.commandBufferCount; i++)
          {
            ObjDisp(queue)->QueueSubmit(Unwrap(queue), 1, &rerecordedSubmit, VK_NULL_HANDLE);
            rerecordedSubmit.pCommandBuffers++;

            FlushQ();
          }
#else
          // don't submit the fence, since we have nothing to wait on it being signalled, and we
          // might not have it correctly in the unsignalled state.
          ObjDisp(queue)->QueueSubmit(Unwrap(queue), 1, &rerecordedSubmit, VK_NULL_HANDLE);
#endif
        }
      }

#if ENABLED(SINGLE_FLUSH_VALIDATE)
      FlushQ();
#endif<commit_msg>Ensure we unwrap pNext chain on replay in vkQueueSubmit<commit_after>/******************************************************************************
 * The MIT License (MIT)
 *
 * Copyright (c) 2015-2018 <NAME>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 ******************************************************************************/

#include "../vk_core.h"

template <typename SerialiserType>
bool WrappedVulkan::Serialise_vkGetDeviceQueue(SerialiserType &ser, VkDevice device,
                                               uint32_t queueFamilyIndex, uint32_t queueIndex,
                                               VkQueue *pQueue)
{
  SERIALISE_ELEMENT(device);
  SERIALISE_ELEMENT(queueFamilyIndex);
  SERIALISE_ELEMENT(queueIndex);
  SERIALISE_ELEMENT_LOCAL(Queue, GetResID(*pQueue)).TypedAs("VkQueue");

  SERIALISE_CHECK_READ_ERRORS();

  if(IsReplayingAndReading())
  {
    VkQueue queue;

    uint32_t remapFamily = m_QueueRemapping[queueFamilyIndex][queueIndex].family;
    uint32_t remapIndex = m_QueueRemapping[queueFamilyIndex][queueIndex].index;

    if(remapFamily != queueFamilyIndex || remapIndex != queueIndex)
      RDCLOG("Remapped Queue %u/%u from capture to %u/%u on replay", queueFamilyIndex, queueIndex,
             remapFamily, remapIndex);

    ObjDisp(device)->GetDeviceQueue(Unwrap(device), remapFamily, remapIndex, &queue);

    GetResourceManager()->WrapResource(Unwrap(device), queue);
    GetResourceManager()->AddLiveResource(Queue, queue);

    if(remapFamily == m_QueueFamilyIdx && m_Queue == VK_NULL_HANDLE)
    {
      m_Queue = queue;

      // we can now submit any cmds that were queued (e.g. from creating debug
      // manager on vkCreateDevice)
      SubmitCmds();
    }

    if(remapFamily < m_ExternalQueues.size())
    {
      if(m_ExternalQueues[remapFamily].queue == VK_NULL_HANDLE)
        m_ExternalQueues[remapFamily].queue = queue;
    }
    else
    {
      RDCERR("Unexpected queue family index %u", remapFamily);
    }

    m_CreationInfo.m_Queue[GetResID(queue)] = remapFamily;

    AddResource(Queue, ResourceType::Queue, "Queue");
    DerivedResource(device, Queue);
  }

  return true;
}

void WrappedVulkan::vkGetDeviceQueue(VkDevice device, uint32_t queueFamilyIndex,
                                     uint32_t queueIndex, VkQueue *pQueue)
{
  SERIALISE_TIME_CALL(
      ObjDisp(device)->GetDeviceQueue(Unwrap(device), queueFamilyIndex, queueIndex, pQueue));

  if(m_SetDeviceLoaderData)
    m_SetDeviceLoaderData(m_Device, *pQueue);
  else
    SetDispatchTableOverMagicNumber(device, *pQueue);

  RDCASSERT(IsCaptureMode(m_State));

  {
    // it's perfectly valid for enumerate type functions to return the same handle
    // each time. If that happens, we will already have a wrapper created so just
    // return the wrapped object to the user and do nothing else
    if(m_QueueFamilies[queueFamilyIndex][queueIndex] != VK_NULL_HANDLE)
    {
      *pQueue = m_QueueFamilies[queueFamilyIndex][queueIndex];
    }
    else
    {
      ResourceId id = GetResourceManager()->WrapResource(Unwrap(device), *pQueue);

      {
        Chunk *chunk = NULL;

        {
          CACHE_THREAD_SERIALISER();

          SCOPED_SERIALISE_CHUNK(VulkanChunk::vkGetDeviceQueue);
          Serialise_vkGetDeviceQueue(ser, device, queueFamilyIndex, queueIndex, pQueue);

          chunk = scope.Get();
        }

        VkResourceRecord *record = GetResourceManager()->AddResourceRecord(*pQueue);
        RDCASSERT(record);

        record->queueFamilyIndex = queueFamilyIndex;

        VkResourceRecord *instrecord = GetRecord(m_Instance);

        // treat queues as pool members of the instance (ie. freed when the instance dies)
        {
          instrecord->LockChunks();
          instrecord->pooledChildren.push_back(record);
          instrecord->UnlockChunks();
        }

        record->AddChunk(chunk);
      }

      m_QueueFamilies[queueFamilyIndex][queueIndex] = *pQueue;

      if(queueFamilyIndex < m_ExternalQueues.size())
      {
        if(m_ExternalQueues[queueFamilyIndex].queue == VK_NULL_HANDLE)
          m_ExternalQueues[queueFamilyIndex].queue = *pQueue;
      }
      else
      {
        RDCERR("Unexpected queue family index %u", queueFamilyIndex);
      }

      if(queueFamilyIndex == m_QueueFamilyIdx)
      {
        m_Queue = *pQueue;

        // we can now submit any cmds that were queued (e.g. from creating debug
        // manager on vkCreateDevice)
        SubmitCmds();
      }
    }
  }
}

template <typename SerialiserType>
bool WrappedVulkan::Serialise_vkQueueSubmit(SerialiserType &ser, VkQueue queue, uint32_t submitCount,
                                            const VkSubmitInfo *pSubmits, VkFence fence)
{
  SERIALISE_ELEMENT(queue);
  SERIALISE_ELEMENT(submitCount);
  SERIALISE_ELEMENT_ARRAY(pSubmits, submitCount);
  SERIALISE_ELEMENT(fence);

  Serialise_DebugMessages(ser);

  SERIALISE_CHECK_READ_ERRORS();

  if(IsReplayingAndReading())
  {
    // if there are multiple queue submissions in flight, wait for the previous queue to finish
    // before executing this, as we don't have the sync information to properly sync.
    if(m_PrevQueue != queue)
    {
      RDCDEBUG("Previous queue execution was on queue %llu, now executing %llu, syncing GPU",
               GetResID(m_PrevQueue), GetResID(queue));
      if(m_PrevQueue != VK_NULL_HANDLE)
        ObjDisp(m_PrevQueue)->QueueWaitIdle(Unwrap(m_PrevQueue));

      m_PrevQueue = queue;
    }

    // if we ever waited on any semaphores, wait for idle here.
    bool doWait = false;
    for(uint32_t i = 0; i < submitCount; i++)
      if(pSubmits[i].waitSemaphoreCount > 0)
        doWait = true;

    if(doWait)
      ObjDisp(queue)->QueueWaitIdle(Unwrap(queue));

    // add a drawcall use for this submission, to tally up with any debug messages that come from it
    if(IsLoading(m_State))
    {
      DrawcallUse use(m_CurChunkOffset, m_RootEventID);

      // insert in sorted location
      auto drawit = std::lower_bound(m_DrawcallUses.begin(), m_DrawcallUses.end(), use);
      m_DrawcallUses.insert(drawit, use);
    }

    for(uint32_t sub = 0; sub < submitCount; sub++)
    {
      VkSubmitInfo submitInfo = pSubmits[sub];
      submitInfo.pWaitSemaphores = NULL;
      submitInfo.waitSemaphoreCount = 0;
      submitInfo.pSignalSemaphores = NULL;
      submitInfo.signalSemaphoreCount = 0;

      if(IsLoading(m_State))
      {
        // don't submit the fence, since we have nothing to wait on it being signalled, and we might
        // not have it correctly in the unsignalled state.
        VkSubmitInfo unwrapped = submitInfo;

        size_t tempMemSize = unwrapped.commandBufferCount * sizeof(VkCommandBuffer) +
                             GetNextPatchSize(unwrapped.pNext);

        byte *tempMem = GetTempMemory(tempMemSize);

        VkCommandBuffer *unwrappedCmds = (VkCommandBuffer *)tempMem;
        unwrapped.pCommandBuffers = unwrappedCmds;
        for(uint32_t i = 0; i < unwrapped.commandBufferCount; i++)
          unwrappedCmds[i] = Unwrap(submitInfo.pCommandBuffers[i]);

        tempMem += unwrapped.commandBufferCount * sizeof(VkCommandBuffer);

        UnwrapNextChain(m_State, "VkSubmitInfo", tempMem, (VkGenericStruct *)&unwrapped);

        ObjDisp(queue)->QueueSubmit(Unwrap(queue), 1, &unwrapped, VK_NULL_HANDLE);

        AddEvent();

        // we're adding multiple events, need to increment ourselves
        m_RootEventID++;

        std::string basename = StringFormat::Fmt("vkQueueSubmit(%u)", submitInfo.commandBufferCount);

        for(uint32_t c = 0; c < submitInfo.commandBufferCount; c++)
        {
          ResourceId cmd =
              GetResourceManager()->GetOriginalID(GetResID(submitInfo.pCommandBuffers[c]));

          BakedCmdBufferInfo &cmdBufInfo = m_BakedCmdBufferInfo[cmd];

          GetResourceManager()->ApplyBarriers(m_CreationInfo.m_Queue[GetResID(queue)],
                                              m_BakedCmdBufferInfo[cmd].imgbarriers, m_ImageLayouts);

          std::string name = StringFormat::Fmt("=> %s[%u]: vkBeginCommandBuffer(%s)",
                                               basename.c_str(), c, ToStr(cmd).c_str());

          // add a fake marker
          DrawcallDescription draw;
          draw.name = name;
          draw.flags |= DrawFlags::SetMarker;
          AddEvent();

          m_RootEvents.back().chunkIndex = cmdBufInfo.beginChunk;
          m_Events.back().chunkIndex = cmdBufInfo.beginChunk;

          AddDrawcall(draw, true);
          m_RootEventID++;

          // insert the baked command buffer in-line into this list of notes, assigning new event
          // and drawIDs
          InsertDrawsAndRefreshIDs(cmdBufInfo.draw->children);

          for(size_t e = 0; e < cmdBufInfo.draw->executedCmds.size(); e++)
          {
            std::vector<Submission> &submits =
                m_Partial[Secondary].cmdBufferSubmits[cmdBufInfo.draw->executedCmds[e]];

            for(size_t s = 0; s < submits.size(); s++)
            {
              if(!submits[s].rebased)
              {
                submits[s].baseEvent += m_RootEventID;
                submits[s].rebased = true;
              }
            }
          }

          for(size_t i = 0; i < cmdBufInfo.debugMessages.size(); i++)
          {
            m_DebugMessages.push_back(cmdBufInfo.debugMessages[i]);
            m_DebugMessages.back().eventId += m_RootEventID;
          }

          // only primary command buffers can be submitted
          m_Partial[Primary].cmdBufferSubmits[cmd].push_back(Submission(m_RootEventID));

          m_RootEventID += cmdBufInfo.eventCount;
          m_RootDrawcallID += cmdBufInfo.drawCount;

          name = StringFormat::Fmt("=> %s[%u]: vkEndCommandBuffer(%s)", basename.c_str(), c,
                                   ToStr(cmd).c_str());
          draw.name = name;
          AddEvent();

          m_RootEvents.back().chunkIndex = cmdBufInfo.endChunk;
          m_Events.back().chunkIndex = cmdBufInfo.endChunk;

          AddDrawcall(draw, true);
          m_RootEventID++;
        }

        // account for the outer loop thinking we've added one event and incrementing,
        // since we've done all the handling ourselves this will be off by one.
        m_RootEventID--;
      }
      else
      {
        // account for the queue submit event
        m_RootEventID++;

        uint32_t startEID = m_RootEventID;

        // advance m_CurEventID to match the events added when reading
        for(uint32_t c = 0; c < submitInfo.commandBufferCount; c++)
        {
          ResourceId cmd =
              GetResourceManager()->GetOriginalID(GetResID(submitInfo.pCommandBuffers[c]));

          // 2 extra for the virtual labels around the command buffer
          m_RootEventID += 2 + m_BakedCmdBufferInfo[cmd].eventCount;
          m_RootDrawcallID += 2 + m_BakedCmdBufferInfo[cmd].drawCount;
        }

        // same accounting for the outer loop as above
        m_RootEventID--;

        if(submitInfo.commandBufferCount == 0)
        {
          // do nothing, don't bother with the logic below
        }
        else if(m_LastEventID <= startEID)
        {
#if ENABLED(VERBOSE_PARTIAL_REPLAY)
          RDCDEBUG("Queue Submit no replay %u == %u", m_LastEventID, startEID);
#endif
        }
        else
        {
#if ENABLED(VERBOSE_PARTIAL_REPLAY)
          RDCDEBUG("Queue Submit from re-recorded commands, root EID %u last EID", m_RootEventID,
                   m_LastEventID);
#endif

          uint32_t eid = startEID;

          std::vector<VkCommandBuffer> rerecordedCmds;

          for(uint32_t c = 0; c < submitInfo.commandBufferCount; c++)
          {
            ResourceId cmdId =
                GetResourceManager()->GetOriginalID(GetResID(submitInfo.pCommandBuffers[c]));

            // account for the virtual vkBeginCommandBuffer label at the start of the events here
            // so it matches up to baseEvent
            eid++;

#if ENABLED(VERBOSE_PARTIAL_REPLAY)
            uint32_t end = eid + m_BakedCmdBufferInfo[cmdId].eventCount;
#endif

            if(eid <= m_LastEventID)
            {
              VkCommandBuffer cmd = RerecordCmdBuf(cmdId);
              ResourceId rerecord = GetResID(cmd);
#if ENABLED(VERBOSE_PARTIAL_REPLAY)
              RDCDEBUG("Queue Submit re-recorded replay of %llu, using %llu (%u -> %u <= %u)",
                       cmdId, rerecord, eid, end, m_LastEventID);
#endif
              rerecordedCmds.push_back(Unwrap(cmd));

              GetResourceManager()->ApplyBarriers(m_CreationInfo.m_Queue[GetResID(queue)],
                                                  m_BakedCmdBufferInfo[rerecord].imgbarriers,
                                                  m_ImageLayouts);
            }
            else
            {
#if ENABLED(VERBOSE_PARTIAL_REPLAY)
              RDCDEBUG("Queue not submitting %llu", cmdId);
#endif
            }

            // 1 extra to account for the virtual end command buffer label (begin is accounted for
            // above)
            eid += 1 + m_BakedCmdBufferInfo[cmdId].eventCount;
          }

          VkSubmitInfo rerecordedSubmit = submitInfo;

          byte *tempMem = GetTempMemory(GetNextPatchSize(rerecordedSubmit.pNext));

          UnwrapNextChain(m_State, "VkSubmitInfo", tempMem, (VkGenericStruct *)&rerecordedSubmit);

          rerecordedSubmit.commandBufferCount = (uint32_t)rerecordedCmds.size();
          rerecordedSubmit.pCommandBuffers = &rerecordedCmds[0];

#if ENABLED(SINGLE_FLUSH_VALIDATE)
          rerecordedSubmit.commandBufferCount = 1;
          for(uint32_t i = 0; i < rerecordedSubmit.commandBufferCount; i++)
          {
            ObjDisp(queue)->QueueSubmit(Unwrap(queue), 1, &rerecordedSubmit, VK_NULL_HANDLE);
            rerecordedSubmit.pCommandBuffers++;

            FlushQ();
          }
#else
          // don't submit the fence, since we have nothing to wait on it being signalled, and we
          // might not have it correctly in the unsignalled state.
          ObjDisp(queue)->QueueSubmit(Unwrap(queue), 1, &rerecordedSubmit, VK_NULL_HANDLE);
#endif
        }
      }

#if ENABLED(SINGLE_FLUSH_VALIDATE)
      FlushQ();
#endif
{'fraction_non_alphanumeric': 0.10322026053031806, 'fraction_numerical': 0.004496462342127885, 'mean_word_length': 2.6055548933126715, 'pattern_counts': {'":': 0, '<': 43, '<?xml version=': 0, '>': 69, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 32, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4886866', 'n_tokens_mistral': 9119, 'n_tokens_neox': 8615, 'n_words': 2081}