text: #include "dxvk_cmdlist.h"
  #include "dxvk_device.h"
  
  namespace dxvk {
      
    DxvkCommandList::DxvkCommandList(
            DxvkDevice*       device,
            uint32_t          queueFamily)
    : m_vkd           (device->vkd()),
      m_cmdBuffersUsed(0),
      m_descriptorPoolTracker(device),
      m_stagingAlloc  (device) {
      VkFenceCreateInfo fenceInfo;
      fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
      fenceInfo.pNext = nullptr;
      fenceInfo.flags = 0;
      
      if (m_vkd->vkCreateFence(m_vkd->device(), &fenceInfo, nullptr, &m_fence) != VK_SUCCESS)
        throw DxvkError("DxvkCommandList: Failed to create fence");
      
      VkCommandPoolCreateInfo poolInfo;
      poolInfo.sType            = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
      poolInfo.pNext            = nullptr;
      poolInfo.flags            = 0;
      poolInfo.queueFamilyIndex = queueFamily;
      
      if (m_vkd->vkCreateCommandPool(m_vkd->device(), &poolInfo, nullptr, &m_pool) != VK_SUCCESS)
        throw DxvkError("DxvkCommandList: Failed to create command pool");
      
      VkCommandBufferAllocateInfo cmdInfo;
      cmdInfo.sType             = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
      cmdInfo.pNext             = nullptr;
      cmdInfo.commandPool       = m_pool;
      cmdInfo.level             = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
      cmdInfo.commandBufferCount = 1;
      
      if (m_vkd->vkAllocateCommandBuffers(m_vkd->device(), &cmdInfo, &m_execBuffer) != VK_SUCCESS
       || m_vkd->vkAllocateCommandBuffers(m_vkd->device(), &cmdInfo, &m_initBuffer) != VK_SUCCESS)
        throw DxvkError("DxvkCommandList: Failed to allocate command buffer");
    }
    
    
    DxvkCommandList::~DxvkCommandList() {
      this->reset();
      
      m_vkd->vkDestroyCommandPool(m_vkd->device(), m_pool,  nullptr);
      m_vkd->vkDestroyFence      (m_vkd->device(), m_fence, nullptr);
    }
    
    
    VkResult DxvkCommandList::submit(
            VkQueue         queue,
            VkSemaphore     waitSemaphore,
            VkSemaphore     wakeSemaphore) {
      std::array<VkCommandBuffer, 2> cmdBuffers;
      uint32_t cmdBufferCount = 0;
      
      if (m_cmdBuffersUsed.test(DxvkCmdBuffer::InitBuffer))
        cmdBuffers[cmdBufferCount++] = m_initBuffer;
      if (m_cmdBuffersUsed.test(DxvkCmdBuffer::ExecBuffer))
        cmdBuffers[cmdBufferCount++] = m_execBuffer;
      
      const VkPipelineStageFlags waitStageMask
        = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
      
      VkSubmitInfo info;
      info.sType                = VK_STRUCTURE_TYPE_SUBMIT_INFO;
      info.pNext                = nullptr;
      info.waitSemaphoreCount   = waitSemaphore == VK_NULL_HANDLE ? 0 : 1;
      info.pWaitSemaphores      = &waitSemaphore;
      info.pWaitDstStageMask    = &waitStageMask;
      info.commandBufferCount   = cmdBufferCount;
      info.pCommandBuffers      = cmdBuffers.data();
      info.signalSemaphoreCount = wakeSemaphore == VK_NULL_HANDLE ? 0 : 1;
      info.pSignalSemaphores    = &wakeSemaphore;
      
      return m_vkd->vkQueueSubmit(queue, 1, &info, m_fence);
    }
    
    
    VkResult DxvkCommandList::synchronize() {
      VkResult status = VK_TIMEOUT;
      
      while (status == VK_TIMEOUT) {
        status = m_vkd->vkWaitForFences(
          m_vkd->device(), 1, &m_fence, VK_FALSE,
          1'000'000'000ull);
      }
      
      return status;
    }
    
    
    void DxvkCommandList::beginRecording() {
      VkCommandBufferBeginInfo info;
      info.sType            = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
      info.pNext            = nullptr;
      info.flags            = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
      info.pInheritanceInfo = nullptr;
      
      if (m_vkd->vkResetCommandPool(m_vkd->device(), m_pool, 0) != VK_SUCCESS)
        Logger::err("DxvkCommandList: Failed to reset command buffer");
      
      if (m_vkd->vkBeginCommandBuffer(m_execBuffer, &info) != VK_SUCCESS
       || m_vkd->vkBeginCommandBuffer(m_initBuffer, &info) != VK_SUCCESS)
        Logger::err("DxvkCommandList: Failed to begin command buffer");
      
      if (m_vkd->vkResetFences(m_vkd->device(), 1, &m_fence) != VK_SUCCESS)
        Logger::err("DxvkCommandList: Failed to reset fence");
      
      // Unconditionally mark the exec buffer as used. There
      // is virtually no use case where this isn't correct.
      m_cmdBuffersUsed.set(DxvkCmdBuffer::ExecBuffer);
    }
    
    
    void DxvkCommandList::endRecording() {
      if (m_vkd->vkEndCommandBuffer(m_execBuffer) != VK_SUCCESS
       || m_vkd->vkEndCommandBuffer(m_initBuffer) != VK_SUCCESS)
        Logger::err("DxvkCommandList::endRecording: Failed to record command buffer");
    }
    
    
    void DxvkCommandList::reset() {
      m_statCounters.reset();
      m_bufferTracker.reset();
      m_gpuQueryTracker.reset();
      m_gpuEventTracker.reset();
      m_eventTracker.reset();
      m_stagingAlloc.reset();
      m_descriptorPoolTracker.reset();
      m_resources.reset();
    }
    
    
    DxvkStagingBufferSlice DxvkCommandList::stagedAlloc(VkDeviceSize size) {
      return m_stagingAlloc.alloc(size);
    }
    
    
    void DxvkCommandList::stagedBufferCopy(
            DxvkCmdBuffer           cmdBuffer,
            VkBuffer                dstBuffer,
            VkDeviceSize            dstOffset,
            VkDeviceSize            dataSize,
      const DxvkStagingBufferSlice& dataSlice) {
      m_cmdBuffersUsed.set(cmdBuffer);
      
      VkBufferCopy region;
      region.srcOffset = dataSlice.offset;
      region.dstOffset = dstOffset;
      region.size      = dataSize;
      
      m_vkd->vkCmdCopyBuffer(getCmdBuffer(cmdBuffer),
        dataSlice.buffer, dstBuffer, 1, &region);
    }
    
    
    void DxvkCommandList::stagedBufferImageCopy(
            VkImage                 dstImage,
            VkImageLayout           dstImageLayout,
      const VkBufferImageCopy&      dstImageRegion,
      const DxvkStagingBufferSlice& dataSlice) {
      m_vkd->vkCmdCopyBufferToImage(m_execBuffer,
        dataSlice.buffer, dstImage, dstImageLayout,
        1, &dstImageRegion);
    }
    
  }