Program Listing for File D3D12DrawablePool.cpp

Return to documentation for file (Source\Azura\RenderSystem\Src\D3D12\D3D12DrawablePool.cpp)

#include "D3D12/D3D12DrawablePool.h"

#include "D3D12/d3dx12.h"
#include "D3D12/D3D12Macros.h"
#include "Memory/MemoryFactory.h"
#include "Memory/MonotonicAllocator.h"
#include "D3D12/D3D12ScopedImage.h"
#include <algorithm>
#include <utility>

using namespace Microsoft::WRL;    // NOLINT
using namespace Azura::Containers; // NOLINT

namespace Azura {
namespace D3D12 {

D3D12DrawablePool::D3D12DrawablePool(const ComPtr<ID3D12Device>& device,
                                     const DrawablePoolCreateInfo& createInfo,
                                     const DescriptorCount& descriptorCount,
                                     const Vector<DescriptorSlot>& descriptorSlots,
                                     const Vector<D3D12ScopedShader>& shaders,
                                     const Vector<D3D12ScopedRenderPass>& renderPasses,
                                     const Vector<D3D12ScopedBuffer>& gpuBuffers,
                                     ComPtr<ID3D12CommandQueue> commandQueue,
                                     Memory::Allocator& mainAllocator,
                                     Memory::Allocator& initAllocator,
                                     Log log)
  : DrawablePool(createInfo, descriptorCount, mainAllocator),
    log_D3D12RenderSystem(std::move(log)),
    m_device(device),
    m_globalDescriptorSlots(descriptorSlots),
    m_shaders(shaders),
    m_pipelines(mainAllocator),
    m_drawables(createInfo.m_numDrawables, mainAllocator),
    m_renderPasses(createInfo.m_renderPasses.GetSize(), mainAllocator),
    m_gpuBuffers(gpuBuffers.GetSize(), mainAllocator),
    m_graphicsCommandQueue(std::move(commandQueue)),
    m_pipelineFactory(initAllocator, log_D3D12RenderSystem),
    m_descriptorsPerDrawable(0),
    m_images(mainAllocator),
    m_samplers(mainAllocator),
    m_secondaryCommandBuffers(createInfo.m_renderPasses.GetSize(), mainAllocator),
    m_allHeaps(mainAllocator) {
  LOG_DBG(log_D3D12RenderSystem, LOG_LEVEL, "Creating D3D12 Drawable Pool");

  m_pipelineFactory.SetRasterizerStage(createInfo.m_cullMode, FrontFace::CounterClockwise);

  for (const auto& inputBuffer : gpuBuffers) {
    m_gpuBuffers.PushBack(inputBuffer);
  }

  CreateRenderPassReferences(createInfo, renderPasses);
  CreateInputAttributes(createInfo);
  CreateDescriptorHeap(createInfo);

  // Create Buffers
  m_stagingBuffer.Create(device, CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD), createInfo.m_byteSize,
                         D3D12_RESOURCE_STATE_GENERIC_READ, log_D3D12RenderSystem);
  m_stagingBuffer.Map();

  m_updateBuffer.Create(device, CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD), createInfo.m_byteSize,
    D3D12_RESOURCE_STATE_GENERIC_READ, log_D3D12RenderSystem);
  m_updateBuffer.Map();

  m_mainBuffer.Create(device, CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT), createInfo.m_byteSize,
                      D3D12_RESOURCE_STATE_COPY_DEST, log_D3D12RenderSystem);
}

DrawableID D3D12DrawablePool::CreateDrawable(const DrawableCreateInfo& createInfo) {
  LOG_DBG(log_D3D12RenderSystem, LOG_LEVEL, "Creating a D3D12 Drawable");

  D3D12Drawable drawable = D3D12Drawable(createInfo, m_numVertexSlots, m_numInstanceSlots,
                                         m_descriptorCount.m_numUniformSlots, GetAllocator());
  m_drawables.PushBack(std::move(drawable));
  return m_drawables.GetSize() - 1;
}

void D3D12DrawablePool::BindVertexData(DrawableID drawableId, SlotID slot, const U8* buffer, U32 size) {
  LOG_DBG(log_D3D12RenderSystem, LOG_LEVEL,
    "D3D12 Drawable Pool: Binding Vertex Requested for Drawable: %d for Slot: %d of Size: %d bytes", drawableId, slot,
    size);

  assert(m_drawables.GetSize() > drawableId);

  auto& drawable = m_drawables[drawableId];

  // TODO(vasumahesh1):[INPUT]: Could be an issue with sizeof(float)
  const U32 offset = m_stagingBuffer.AppendData(buffer, size, sizeof(float), log_D3D12RenderSystem);

  BufferInfo info    = BufferInfo();
  info.m_maxByteSize = size;
  info.m_byteSize    = size;
  info.m_offset      = offset;
  info.m_binding     = slot;

  drawable.AddVertexBufferInfo(std::move(info));
}

void D3D12DrawablePool::BindVertexData(DrawableID drawableId, SlotID slot, U32 sourceBuffer, U32 offset, U32 size) {
  LOG_DBG(log_D3D12RenderSystem, LOG_LEVEL,
    "D3D12 Drawable Pool: Binding Vertex Requested for Drawable: %d for Slot: %d of Size: %d bytes", drawableId, slot,
    size);

  assert(m_drawables.GetSize() > drawableId);

  auto& drawable = m_drawables[drawableId];

  BufferInfo info    = BufferInfo();
  info.m_maxByteSize = size;
  info.m_byteSize    = size;
  info.m_offset      = offset;
  info.m_binding     = slot;
  info.m_sourceBufferId = sourceBuffer;

  drawable.AddVertexBufferInfo(std::move(info));
}

void D3D12DrawablePool::BindInstanceData(DrawableID drawableId, SlotID slot, const U8* buffer, U32 size) {
  LOG_DBG(log_D3D12RenderSystem, LOG_LEVEL,
    "D3D12 Drawable Pool: Binding Instance Requested for Drawable: %d for Slot: %d of Size: %d bytes", drawableId, slot,
    size);

  assert(m_drawables.GetSize() > drawableId);

  auto& drawable = m_drawables[drawableId];

  // TODO(vasumahesh1):[INPUT]: Could be an issue with sizeof(float)
  const U32 offset = m_stagingBuffer.AppendData(buffer, size, sizeof(float), log_D3D12RenderSystem);

  BufferInfo info    = BufferInfo();
  info.m_maxByteSize = size;
  info.m_byteSize    = size;
  info.m_offset      = offset;
  info.m_binding     = slot;

  drawable.AddInstanceBufferInfo(std::move(info));
}

void D3D12DrawablePool::BindUniformData(DrawableID drawableId, SlotID slot, const U8* buffer, U32 size) {
  LOG_DBG(log_D3D12RenderSystem, LOG_LEVEL,
    "D3D12 Drawable Pool: Binding Uniform Requested for Drawable: %d for Slot: %d of Size: %d bytes", drawableId, slot,
    size);

  assert(m_drawables.GetSize() > drawableId);

  auto& drawable = m_drawables[drawableId];

  size = (size + D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT - 1) & ~(
           D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT - 1);

  // TODO(vasumahesh1):[INPUT]: Could be an issue with sizeof(float)
  const U32 offset = m_stagingBuffer.AppendData(buffer, size, D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT,
                                                log_D3D12RenderSystem);

  const auto& descriptorSlot = m_globalDescriptorSlots[slot];

  UniformBufferInfo info = UniformBufferInfo();
  info.m_byteSize        = size;
  info.m_offset          = offset;
  info.m_binding         = descriptorSlot.m_bindIdx;
  info.m_set             = descriptorSlot.m_setIdx;

  drawable.AddUniformBufferInfo(std::move(info));
}

void D3D12DrawablePool::SetIndexData(DrawableID drawableId, const U8* buffer, U32 size) {
  LOG_DBG(log_D3D12RenderSystem, LOG_LEVEL,
    "D3D12 Drawable Pool: Binding Index Requested for Drawable: %d of Size: %d bytes", drawableId, size);

  auto& drawable = m_drawables[drawableId];

  const U32 offset = m_stagingBuffer.AppendData(buffer, size, sizeof(U32), log_D3D12RenderSystem);

  BufferInfo info = BufferInfo();
  info.m_byteSize = size;
  info.m_offset   = offset;

  drawable.SetIndexBufferInfo(std::move(info));
}

void D3D12DrawablePool::AddShader(U32 shaderId) {
  LOG_DBG(log_D3D12RenderSystem, LOG_LEVEL, "D3D12 Drawable Pool: Binding Shader Requested, ID: %d", shaderId);
  m_pipelineFactory.AddShaderStage(m_shaders[shaderId]);
}

void D3D12DrawablePool::BindTextureData(SlotID slot, const TextureDesc& desc, const U8* buffer) {
  LOG_DBG(log_D3D12RenderSystem, LOG_LEVEL,
    "D3D12 Drawable Pool: Binding Texture Requested for Slot: %d of Size: %d bytes", slot, desc.m_size);

  const U32 size = desc.m_size;

  const auto& descriptorSlot = m_globalDescriptorSlots[slot];

  const U32 textureWidthBytes = desc.m_bounds.m_width * GetFormatSize(desc.m_format);

  // TODO(vasumahesh1):[INPUT]: Could be an issue with sizeof(float)
  const U32 offset = m_stagingBuffer.AppendTextureData(buffer, size, 512, textureWidthBytes, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT, log_D3D12RenderSystem);

  TextureBufferInfo info = TextureBufferInfo();
  info.m_byteSize        = size;
  info.m_offset          = offset;
  info.m_desc            = desc;
  info.m_binding         = descriptorSlot.m_bindIdx;
  info.m_set             = descriptorSlot.m_setIdx;

  m_textureBufferInfos.PushBack(info);
}

void D3D12DrawablePool::BindSampler(SlotID slot, const SamplerDesc& desc) {
  LOG_DBG(log_D3D12RenderSystem, LOG_LEVEL, "D3D12 Drawable Pool: Binding Sampler Requested for Slot: %d", slot);

  const auto& descriptorSlot = m_globalDescriptorSlots[slot];
  assert(descriptorSlot.m_type == DescriptorType::Sampler);

  SamplerInfo sInfo = {};
  sInfo.m_set       = descriptorSlot.m_setIdx;
  sInfo.m_binding   = descriptorSlot.m_bindIdx;
  sInfo.m_desc      = desc;

  m_samplerInfos.PushBack(sInfo);
}

void D3D12DrawablePool::SetTextureData(ID3D12GraphicsCommandList* oneTimeCommandList) {
  if (m_textureBufferInfos.GetSize() == 0) {
    return;
  }

  LOG_DBG(log_D3D12RenderSystem, LOG_LEVEL, "D3D12 Drawable Pool: Texture Data Found");

  const CD3DX12_CPU_DESCRIPTOR_HANDLE textureCPUHandle(m_descriptorDrawableHeap->GetCPUDescriptorHandleForHeapStart());

  m_images.Reserve(m_textureBufferInfos.GetSize());

  U32 idx = 0;
  for (const auto& textBufInfo : m_textureBufferInfos) {

    D3D12ScopedImage image = {};
    image.Create(m_device, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_FLAG_NONE, textBufInfo.m_desc,
                 log_D3D12RenderSystem);
    image.CopyFromBuffer(m_device, oneTimeCommandList, m_mainBuffer, textBufInfo.m_offset);
    image.Transition(oneTimeCommandList, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, log_D3D12RenderSystem);

    m_images.PushBack(std::move(image));

    CD3DX12_CPU_DESCRIPTOR_HANDLE cpuHandle;
    CD3DX12_CPU_DESCRIPTOR_HANDLE::InitOffsetted(cpuHandle, textureCPUHandle, m_cbvSrvDescriptorElementSize * idx);

    const auto srvDesc = D3D12ScopedImage::
      GetSRV(textBufInfo.m_desc.m_format, ImageViewType::ImageView2D, log_D3D12RenderSystem);
    m_device->CreateShaderResourceView(m_images.Last().Real(), &srvDesc, cpuHandle);
    ++idx;
  }
}

void D3D12DrawablePool::SetSamplerData() {
  if (m_samplerInfos.GetSize() == 0) {
    return;
  }

  const CD3DX12_CPU_DESCRIPTOR_HANDLE samplerCPUHandle(m_descriptorSamplerHeap->GetCPUDescriptorHandleForHeapStart());

  m_samplers.Reserve(m_samplerInfos.GetSize());

  U32 idx = 0;
  for (const auto& samplerInfo : m_samplerInfos) {
    D3D12ScopedSampler sampler = {};
    sampler.Create(samplerInfo.m_desc, log_D3D12RenderSystem);

    CD3DX12_CPU_DESCRIPTOR_HANDLE cpuHandle;
    CD3DX12_CPU_DESCRIPTOR_HANDLE::InitOffsetted(cpuHandle, samplerCPUHandle, m_samplerDescriptorElementSize * idx);

    m_device->CreateSampler(&sampler.GetDesc(), cpuHandle);

    ++idx;
  }
}

void D3D12DrawablePool::CreateRenderPassInputTargetSRV(
  const Vector<std::reference_wrapper<const D3D12ScopedImage>>& renderPassInputs,
  U32 offsetTillThis) const {
  const CD3DX12_CPU_DESCRIPTOR_HANDLE inputCPUHandle(m_descriptorDrawableHeap->GetCPUDescriptorHandleForHeapStart(),
                                                     m_offsetToRenderPassInputs + offsetTillThis,
                                                     m_cbvSrvDescriptorElementSize);

  U32 idx = 0;
  for (const auto& imageRef : renderPassInputs) {
    CD3DX12_CPU_DESCRIPTOR_HANDLE cpuHandle;
    CD3DX12_CPU_DESCRIPTOR_HANDLE::InitOffsetted(cpuHandle, inputCPUHandle, m_cbvSrvDescriptorElementSize * idx);

    const auto srvDesc = D3D12ScopedImage::GetSRV(imageRef.get().GetFormat(), ImageViewType::ImageView2D,
                                                  log_D3D12RenderSystem);
    m_device->CreateShaderResourceView(imageRef.get().Real(), &srvDesc, cpuHandle);
    ++idx;
  }
}

void D3D12DrawablePool::Submit() {
  m_cbvSrvDescriptorElementSize  = m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
  m_samplerDescriptorElementSize = m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);

  std::sort(m_textureBufferInfos.Begin(), m_textureBufferInfos.End(),
            [](const TextureBufferInfo& a, const TextureBufferInfo& b) -> bool
            {
              if (a.m_set == b.m_set) {
                return a.m_binding < b.m_binding;
              }

              return a.m_set < b.m_set;
            });

  std::sort(m_samplerInfos.Begin(), m_samplerInfos.End(), [](const SamplerInfo& a, const SamplerInfo& b) -> bool
  {
    if (a.m_set == b.m_set) {
      return a.m_binding < b.m_binding;
    }

    return a.m_set < b.m_set;
  });

  U32 inputsTillNow = 0;
  for (U32 idx      = 0; idx < m_renderPasses.GetSize(); ++idx) {
    D3D12ScopedCommandBuffer cmdBuffer(m_device, D3D12_COMMAND_LIST_TYPE_BUNDLE, log_D3D12RenderSystem);
    m_secondaryCommandBuffers.PushBack(cmdBuffer);

    const auto& renderPassInputs = m_renderPasses[idx].get().GetInputImages();

    if (renderPassInputs.GetSize() > 0) {
      CreateRenderPassInputTargetSRV(renderPassInputs, inputsTillNow);
      inputsTillNow += renderPassInputs.GetSize();
    }
  }

  m_pipelines.Reserve(m_renderPasses.GetSize());

  LOG_DBG(log_D3D12RenderSystem, LOG_LEVEL, "D3D12 Drawable Pool: Submitting");
  m_pipelineFactory.Submit(m_device, m_renderPasses, m_pipelines);

  auto oneTimeSubmitBuffer = D3D12ScopedCommandBuffer(m_device, D3D12_COMMAND_LIST_TYPE_DIRECT, log_D3D12RenderSystem);
  oneTimeSubmitBuffer.CreateGraphicsCommandList(m_device, nullptr, log_D3D12RenderSystem);

  auto oneTimeCommandList = oneTimeSubmitBuffer.GetGraphicsCommandList();

  const UINT64 stagingBufferSize = GetRequiredIntermediateSize(m_stagingBuffer.Real(), 0, 1);
  D3D12Core::CopyBuffer(oneTimeCommandList, m_mainBuffer, m_stagingBuffer, stagingBufferSize);

  m_mainBuffer.Transition(oneTimeCommandList, D3D12_RESOURCE_STATE_COPY_SOURCE, log_D3D12RenderSystem);

  SetTextureData(oneTimeCommandList);
  SetSamplerData();

  m_mainBuffer.Transition(oneTimeCommandList, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER, log_D3D12RenderSystem);

  oneTimeCommandList->Close();

  oneTimeSubmitBuffer.Execute(m_device, m_graphicsCommandQueue.Get(), log_D3D12RenderSystem);
  oneTimeSubmitBuffer.WaitForComplete(m_graphicsCommandQueue.Get(), log_D3D12RenderSystem);

  LOG_DBG(log_D3D12RenderSystem, LOG_LEVEL, "D3D12 Drawable Pool: Created Pipelines");

  const U32 drawableHeapOffset = m_descriptorsPerDrawable * m_cbvSrvDescriptorElementSize;

  CD3DX12_CPU_DESCRIPTOR_HANDLE heapHandle(m_descriptorDrawableHeap->GetCPUDescriptorHandleForHeapStart(),
                                           m_offsetToDrawableHeap, m_cbvSrvDescriptorElementSize);

  LOG_DBG(log_D3D12RenderSystem, LOG_LEVEL, "D3D12 Drawable Pool: Creating Resource Views");

  for (auto& drawable : m_drawables) {
    drawable.CreateResourceViews(m_device, m_mainBuffer.Real(), m_vertexDataSlots, m_gpuBuffers, heapHandle,
                                 m_cbvSrvDescriptorElementSize, log_D3D12RenderSystem);
    heapHandle.Offset(drawableHeapOffset);
  }

  // Record Command Buffer

  LOG_DBG(log_D3D12RenderSystem, LOG_LEVEL, "D3D12 Drawable Pool: Begin Recording");

  U32 idx = 0;

  U32 sampledImageRecorded = 0;
  U32 inputsRecorded       = 0;
  U32 samplersRecorded     = 0;
  for (const auto& renderPassRef : m_renderPasses) {
    const auto& renderPass                   = renderPassRef.get();
    const auto& renderPassDescriptorCount    = renderPass.GetDescriptorCount();
    const auto& renderPassRootSignatureTable = renderPass.GetRootSignatureTable();
    const auto& renderPassInputs             = renderPass.GetInputInfo();
    m_secondaryCommandBuffers[idx].CreateGraphicsCommandList(m_device, m_pipelines[idx].GetState(),
                                                             log_D3D12RenderSystem);
    auto bundleCommandList = m_secondaryCommandBuffers[idx].GetGraphicsCommandList();

    // Define Heap Handles
    CD3DX12_GPU_DESCRIPTOR_HANDLE textureGPUHandle(m_descriptorDrawableHeap->GetGPUDescriptorHandleForHeapStart(),
                                                   sampledImageRecorded, m_cbvSrvDescriptorElementSize);
    const CD3DX12_GPU_DESCRIPTOR_HANDLE inputsGPUHandle(m_descriptorDrawableHeap->GetGPUDescriptorHandleForHeapStart(),
                                                        m_offsetToRenderPassInputs + inputsRecorded,
                                                        m_cbvSrvDescriptorElementSize);

    CD3DX12_GPU_DESCRIPTOR_HANDLE samplerGPUHandle;
    if (renderPassDescriptorCount.m_numSamplerSlots > 0) {
      samplerGPUHandle = CD3DX12_GPU_DESCRIPTOR_HANDLE(m_descriptorSamplerHeap->GetGPUDescriptorHandleForHeapStart(),
                                                       samplersRecorded, m_samplerDescriptorElementSize);
    }

    CD3DX12_GPU_DESCRIPTOR_HANDLE uboGPUHandle(m_descriptorDrawableHeap->GetGPUDescriptorHandleForHeapStart(),
                                               m_offsetToDrawableHeap, m_cbvSrvDescriptorElementSize);

    bundleCommandList->SetDescriptorHeaps(UINT(m_allHeaps.GetSize()), m_allHeaps.Data());
    bundleCommandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    bundleCommandList->SetGraphicsRootSignature(renderPass.GetRootSignature());

    U32 tableIdx = 0;
    for (const auto& tableEntry : renderPassRootSignatureTable) {
      if (tableEntry.m_type == DescriptorType::SampledImage) {
        LOG_DEBUG(log_D3D12RenderSystem, LOG_LEVEL, "Setting Texture Descriptor Table at %d", tableIdx);
        bundleCommandList->SetGraphicsRootDescriptorTable(tableIdx, textureGPUHandle);
        textureGPUHandle.Offset(tableEntry.m_count, m_cbvSrvDescriptorElementSize);
      } else if (tableEntry.m_type == DescriptorType::Sampler) {
        LOG_DEBUG(log_D3D12RenderSystem, LOG_LEVEL, "Setting Sampler Descriptor Table at %d", tableIdx);
        bundleCommandList->SetGraphicsRootDescriptorTable(tableIdx, samplerGPUHandle);
        samplerGPUHandle.Offset(tableEntry.m_count, m_samplerDescriptorElementSize);
      }

      ++tableIdx;
    }

    if (renderPassInputs.GetSize() > 0) {
      bundleCommandList->SetGraphicsRootDescriptorTable(renderPassRootSignatureTable.GetSize(), inputsGPUHandle);
      LOG_DEBUG(log_D3D12RenderSystem, LOG_LEVEL, "Setting Input Attachment Descriptor Table at %d",
        renderPassRootSignatureTable.GetSize());
    }

    inputsRecorded += renderPassInputs.GetSize();
    samplersRecorded += renderPassDescriptorCount.m_numSamplerSlots;
    sampledImageRecorded += renderPassDescriptorCount.m_numSampledImageSlots;

    LOG_DBG(log_D3D12RenderSystem, LOG_LEVEL, "D3D12 Drawable Pool: Recording Commands For Drawables");

    for (auto& drawable : m_drawables) {
      drawable.RecordCommands(bundleCommandList, uboGPUHandle, m_cbvSrvDescriptorElementSize,
                              renderPassRootSignatureTable, log_D3D12RenderSystem);
      uboGPUHandle.Offset(drawableHeapOffset);
    }

    LOG_DBG(log_D3D12RenderSystem, LOG_LEVEL, "D3D12 Drawable Pool: Closing Bundle Command Buffer");
    VERIFY_D3D_OP(log_D3D12RenderSystem, bundleCommandList->Close(), "Failed to close bundle Command Buffer");

    ++idx;
  }
}

void D3D12DrawablePool::BeginUpdates() {
  m_updateBuffer.Reset();
  m_bufferUpdates.Reset();
}

void D3D12DrawablePool::UpdateUniformData(DrawableID drawableId, SlotID slot, const U8* buffer, U32 size) {
  LOG_DBG(log_D3D12RenderSystem, LOG_LEVEL,
    "D3D12 Drawable Pool: Update Uniform Requested for Drawable: %d for Slot: %d of Size: %d bytes", drawableId, slot,
    size);

  assert(m_drawables.GetSize() > drawableId);

  auto& drawable = m_drawables[drawableId];

  size = (size + D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT - 1) & ~(D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT - 1);

  const U32 offset = m_updateBuffer.AppendData(buffer, size, D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT, log_D3D12RenderSystem);

  const auto& descriptorSlot = m_globalDescriptorSlots[slot];
  const U32 bufferId = drawable.GetSingleUniformBufferInfo(descriptorSlot);

  const auto& allUboInfos = drawable.GetUniformBufferInfos();

  BufferUpdate info = {};
  info.m_type = BufferUpdateType::UniformBuffer;
  info.m_idx = bufferId;
  info.m_updateOffset = offset;
  info.m_updateByteSize = size;
  info.m_gpuOffset = allUboInfos[bufferId].m_offset;
  info.m_gpuByteSize = allUboInfos[bufferId].m_byteSize;

  m_bufferUpdates.PushBack(info);
}

void D3D12DrawablePool::UpdateTextureData(SlotID slot, const U8* buffer) {
  const auto& descriptorSlot = m_globalDescriptorSlots[slot];
  const U32 bufferId = GetSingleTextureBufferInfo(descriptorSlot);

  const TextureDesc& desc = m_textureBufferInfos[bufferId].m_desc;

  LOG_DBG(log_D3D12RenderSystem, LOG_LEVEL,
    "D3D12 Drawable Pool: Updating Texture Requested for Slot: %d of Size: %d bytes", slot, desc.m_size);

  const U32 textureWidthBytes = desc.m_bounds.m_width * GetFormatSize(desc.m_format);

  // TODO(vasumahesh1):[INPUT]: Could be an issue with sizeof(float)
  const U32 offset = m_updateBuffer.AppendTextureData(buffer, desc.m_size, 512, textureWidthBytes, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT, log_D3D12RenderSystem);

  BufferUpdate info = {};
  info.m_type = BufferUpdateType::SampledImage;
  info.m_idx = bufferId;
  info.m_updateOffset = offset;
  info.m_updateByteSize = desc.m_size;
  info.m_gpuOffset = m_textureBufferInfos[bufferId].m_offset;
  info.m_gpuByteSize = m_textureBufferInfos[bufferId].m_byteSize;

  m_bufferUpdates.PushBack(info);

}

void D3D12DrawablePool::UpdateVertexData(DrawableID drawableId, SlotID slot, const U8* buffer, U32 size) {
  LOG_DBG(log_D3D12RenderSystem, LOG_LEVEL,
    "D3D12 Drawable Pool: Update Uniform Requested for Drawable: %d for Slot: %d of Size: %d bytes", drawableId, slot,
    size);

  assert(m_drawables.GetSize() > drawableId);

  auto& drawable = m_drawables[drawableId];

  const U32 offset = m_updateBuffer.AppendData(buffer, size, sizeof(float), log_D3D12RenderSystem);

  const U32 bufferInfoId = drawable.GetSingleVertexBufferInfo(slot);

  const auto& allVertexBufferInfos = drawable.GetVertexBufferInfos();

  BufferUpdate info = {};
  info.m_type = BufferUpdateType::Vertex;
  info.m_idx = bufferInfoId;
  info.m_updateOffset = offset;
  info.m_updateByteSize = size;
  info.m_gpuOffset = allVertexBufferInfos[bufferInfoId].m_offset;
  info.m_gpuByteSize = allVertexBufferInfos[bufferInfoId].m_byteSize;

  m_bufferUpdates.PushBack(info);
}

void D3D12DrawablePool::UpdateInstanceData(DrawableID drawableId, SlotID slot, const U8* buffer, U32 size) {
  LOG_DBG(log_D3D12RenderSystem, LOG_LEVEL,
    "D3D12 Drawable Pool: Update Uniform Requested for Drawable: %d for Slot: %d of Size: %d bytes", drawableId, slot,
    size);

  assert(m_drawables.GetSize() > drawableId);

  auto& drawable = m_drawables[drawableId];

  const U32 offset = m_updateBuffer.AppendData(buffer, size, sizeof(float), log_D3D12RenderSystem);

  const U32 bufferInfoId = drawable.GetSingleInstanceBufferInfo(slot);

  const auto& allInstanceBufferInfos = drawable.GetInstanceBufferInfos();

  BufferUpdate info = {};
  info.m_type = BufferUpdateType::Instance;
  info.m_idx = bufferInfoId;
  info.m_updateOffset = offset;
  info.m_updateByteSize = size;
  info.m_gpuOffset = allInstanceBufferInfos[bufferInfoId].m_offset;
  info.m_gpuByteSize = allInstanceBufferInfos[bufferInfoId].m_byteSize;

  m_bufferUpdates.PushBack(info);
}

void D3D12DrawablePool::SubmitUpdates() {
  auto oneTimeSubmitBuffer = D3D12ScopedCommandBuffer(m_device, D3D12_COMMAND_LIST_TYPE_DIRECT, log_D3D12RenderSystem);
  oneTimeSubmitBuffer.CreateGraphicsCommandList(m_device, nullptr, log_D3D12RenderSystem);

  auto oneTimeCommandList = oneTimeSubmitBuffer.GetGraphicsCommandList();

  m_mainBuffer.Transition(oneTimeCommandList, D3D12_RESOURCE_STATE_COPY_DEST, log_D3D12RenderSystem);

  // Copy Custom Regions
  for(const auto& updateRegion : m_bufferUpdates)
  {
    if (updateRegion.m_type == BufferUpdateType::UniformBuffer) {
      oneTimeCommandList->CopyBufferRegion(m_mainBuffer.Real(), updateRegion.m_gpuOffset, m_updateBuffer.Real(), updateRegion.m_updateOffset, updateRegion.m_updateByteSize);
    }
    else if (updateRegion.m_type == BufferUpdateType::Vertex || updateRegion.m_type == BufferUpdateType::Instance) {
      oneTimeCommandList->CopyBufferRegion(m_mainBuffer.Real(), updateRegion.m_gpuOffset, m_updateBuffer.Real(), updateRegion.m_updateOffset, updateRegion.m_updateByteSize);
    }
    else if (updateRegion.m_type == BufferUpdateType::SampledImage)
    {
      const auto& targetImage = m_images[updateRegion.m_idx];
      targetImage.Transition(oneTimeCommandList, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_COPY_DEST, log_D3D12RenderSystem);
      targetImage.CopyFromBuffer(m_device, oneTimeCommandList, m_updateBuffer, updateRegion.m_updateOffset);
      targetImage.Transition(oneTimeCommandList, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, log_D3D12RenderSystem);
    }
  }

  m_mainBuffer.Transition(oneTimeCommandList, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER, log_D3D12RenderSystem);

  oneTimeCommandList->Close();

  oneTimeSubmitBuffer.Execute(m_device, m_graphicsCommandQueue.Get(), log_D3D12RenderSystem);
  oneTimeSubmitBuffer.WaitForComplete(m_graphicsCommandQueue.Get(), log_D3D12RenderSystem);
}

const Vector<ID3D12DescriptorHeap*>& D3D12DrawablePool::GetAllDescriptorHeaps() const {
  return m_allHeaps;
}

ID3D12PipelineState* D3D12DrawablePool::GetPipelineState(U32 renderPassId) const {
  return m_pipelines[renderPassId].GetState();
}

ID3D12GraphicsCommandList* D3D12DrawablePool::GetSecondaryCommandList(U32 renderPassId) const {
  return m_secondaryCommandBuffers[renderPassId].GetGraphicsCommandList();
}

void D3D12DrawablePool::GetRecordEntries(Vector<std::pair<U32, D3D12RenderPassRecordEntry>>& recordList) const {
  recordList.Reserve(m_renderPasses.GetSize());

  U32 idx = 0;
  for (const auto& renderPass : m_renderPasses) {
    D3D12RenderPassRecordEntry entry = {};
    entry.m_bundle                   = GetSecondaryCommandList(idx);
    entry.m_pso                      = GetPipelineState(idx);

    recordList.PushBack(std::make_pair(renderPass.get().GetInternalId(), entry));
  }

  ++idx;
}

void D3D12DrawablePool::CreateRenderPassReferences(const DrawablePoolCreateInfo& createInfo,
                                                   const Containers::Vector<D3D12ScopedRenderPass>& renderPasses) {

  U32 idx = 0;
  for (const auto& renderPass : renderPasses) {
    auto it = std::find_if(createInfo.m_renderPasses.Begin(), createInfo.m_renderPasses.End(), [&](U32 passId)
    {
      return renderPass.GetId() == passId;
    });

    if (it != createInfo.m_renderPasses.End()) {
      LOG_DBG(log_D3D12RenderSystem, LOG_LEVEL, "Adding Pool for Render Pass: %d", idx);
      m_renderPasses.PushBack(std::reference_wrapper<D3D12ScopedRenderPass>(renderPasses[idx]));
    }

    ++idx;
  }

  LOG_DBG(log_D3D12RenderSystem, LOG_LEVEL, "Finished Adding Render Passes");
}

void D3D12DrawablePool::CreateInputAttributes(const DrawablePoolCreateInfo& createInfo) {
  LOG_DBG(log_D3D12RenderSystem, LOG_LEVEL, "Adding Input Attributes to Pipeline");

  U32 idx = 0;
  for (const auto& vertexSlot : createInfo.m_vertexDataSlots) {
    m_pipelineFactory.BulkAddAttributeDescription(vertexSlot, idx);
    ++idx;
  }

  LOG_DBG(log_D3D12RenderSystem, LOG_LEVEL, "Finished Adding Input Attributes to Pipeline");
}

void D3D12DrawablePool::CreateDescriptorHeap(const DrawablePoolCreateInfo& createInfo) {
  m_allHeaps.Reserve(2);

  m_offsetToDrawableHeap     = 0;
  m_offsetToRenderPassInputs = 0;

  U32 numSamplers = 0;

  for (const auto& renderPass : m_renderPasses) {
    const auto& count = renderPass.get().GetDescriptorCount();
    m_offsetToDrawableHeap += count.m_numSampledImageSlots;
    m_offsetToDrawableHeap += renderPass.get().GetInputInfo().GetSize(); // RTV as inputs

    m_offsetToRenderPassInputs += count.m_numSampledImageSlots;

    m_descriptorsPerDrawable += count.m_numUniformSlots;
    numSamplers += count.m_numSamplerSlots;
  }

  LOG_DBG(log_D3D12RenderSystem, LOG_LEVEL, "Allocating Descriptor Heaps");
  LOG_DBG(log_D3D12RenderSystem, LOG_LEVEL, "Offset to Drawable: %d", m_offsetToDrawableHeap);
  LOG_DBG(log_D3D12RenderSystem, LOG_LEVEL, "Offset to Render Pass: %d", m_offsetToRenderPassInputs);
  LOG_DBG(log_D3D12RenderSystem, LOG_LEVEL, "Num Samplers: %d", numSamplers);

  D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
  heapDesc.NumDescriptors             = m_offsetToDrawableHeap + createInfo.m_numDrawables * m_descriptorsPerDrawable;
  heapDesc.Type                       = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
  heapDesc.Flags                      = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
  VERIFY_D3D_OP(log_D3D12RenderSystem, m_device->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&m_descriptorDrawableHeap)
  ), "Failed to create Drawable Descriptor Heap");
  m_allHeaps.PushBack(m_descriptorDrawableHeap.Get());

  if (numSamplers > 0) {
    D3D12_DESCRIPTOR_HEAP_DESC samplerDesc = {};
    samplerDesc.NumDescriptors             = numSamplers;
    samplerDesc.Type                       = D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER;
    samplerDesc.Flags                      = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
    VERIFY_D3D_OP(log_D3D12RenderSystem, m_device->CreateDescriptorHeap(&samplerDesc, IID_PPV_ARGS(&
      m_descriptorSamplerHeap)), "Failed to create Sampler Descriptor Heap");

    m_allHeaps.PushBack(m_descriptorSamplerHeap.Get());
  }
}
} // namespace D3D12
} // namespace Azura