Program Listing for File D3D12Renderer.cpp

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

#include "D3D12/D3D12Renderer.h"
#include "D3D12/D3D12Macros.h"

#include "D3D12/d3dx12.h"
#include "Memory/MemoryFactory.h"
#include <array>
#include "Log/Log.h"

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

namespace Azura {
namespace D3D12 {

D3D12Renderer::D3D12Renderer(const ApplicationInfo& appInfo,
                             const DeviceRequirements& deviceRequirements,
                             const ApplicationRequirements& appRequirements,
                             const SwapChainRequirements& swapChainRequirements,
                             const RenderPassRequirements& renderPassRequirements,
                             const DescriptorRequirements& descriptorRequirements,
                             const ShaderRequirements& shaderRequirements,
                             Memory::Allocator& mainAllocator,
                             Memory::Allocator& drawAllocator,
                             Window& window)
  : Renderer(appInfo, deviceRequirements, appRequirements, swapChainRequirements, descriptorRequirements,
             mainAllocator, drawAllocator, window),
    log_D3D12RenderSystem(Log("D3D12RenderSystem")),
    m_perFrameBuffer(8192),
    m_perFrameAllocator(m_perFrameBuffer, 8192),
    m_initBuffer(0x400000),
    m_initAllocator(m_initBuffer, 0x400000),
    m_renderSequence(renderPassRequirements.m_passSequence.GetSize(), mainAllocator),
    m_renderTargetUpdates(renderPassRequirements.m_targets.GetSize(), mainAllocator),
    m_bufferTargetUpdates(renderPassRequirements.m_buffers.GetSize(), mainAllocator),
    m_targetImages(renderPassRequirements.m_targets.GetSize(), mainAllocator),
    m_targetBuffers(renderPassRequirements.m_buffers.GetSize(), mainAllocator),
    m_renderPasses(renderPassRequirements.m_passSequence.GetSize(), mainAllocator),
    m_computePasses(renderPassRequirements.m_passSequence.GetSize(), mainAllocator),
    m_drawablePools(renderPassRequirements.m_maxPools, drawAllocator),
    m_computePools(renderPassRequirements.m_maxPools, drawAllocator),
    m_shaders(shaderRequirements.m_shaders.GetSize(), mainAllocator) {

  const auto viewportDimensions = window.GetViewport();
  m_viewport                    = CD3DX12_VIEWPORT(0.0f, 0.0f, viewportDimensions.m_width, viewportDimensions.m_height);
  m_scissorRect                 = CD3DX12_RECT(0, 0, static_cast<LONG>(viewportDimensions.m_width),
                                               static_cast<LONG>(viewportDimensions.m_height));

#ifdef BUILD_DEBUG
  UINT dxgiFactoryFlags = 0;
  ComPtr<ID3D12Debug> debugController;
  ComPtr<ID3D12Debug1> debugController1;
  if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController)))) {
    debugController->EnableDebugLayer();

    debugController->QueryInterface(IID_PPV_ARGS(&debugController1));
    debugController1->SetEnableGPUBasedValidation(true); // NOLINT

    // Enable additional debug layers.
    dxgiFactoryFlags |= DXGI_CREATE_FACTORY_DEBUG;
  }
#elif defined(BUILD_RELEASE)
  const UINT dxgiFactoryFlags = 0;
#endif

  ComPtr<IDXGIFactory4> factory;
  VERIFY_D3D_OP(log_D3D12RenderSystem, CreateDXGIFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&factory)),
    "Failed to create DXGI Factory");

  ComPtr<IDXGIAdapter1> hardwareAdapter;
  D3D12Core::GetHardwareAdapter(factory.Get(), &hardwareAdapter, log_D3D12RenderSystem);

#ifdef BUILD_DEBUG
  ComPtr<IDXGIAdapter> warpAdapter;
  VERIFY_D3D_OP(log_D3D12RenderSystem, factory->EnumWarpAdapter(IID_PPV_ARGS(&warpAdapter)), "Failed to create Warp adapter");
#endif

  VERIFY_D3D_OP(log_D3D12RenderSystem, D3D12CreateDevice(hardwareAdapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&
    m_device)), "Failed to create D3D12 Device");

  // Describe and create the command queue.
  D3D12_COMMAND_QUEUE_DESC queueDesc = {};
  queueDesc.Flags                    = D3D12_COMMAND_QUEUE_FLAG_NONE;
  queueDesc.Type                     = D3D12_COMMAND_LIST_TYPE_DIRECT;

  VERIFY_D3D_OP(log_D3D12RenderSystem, m_device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&m_mainGraphicsCommandQueue)),
    "Failed to create command Queue");

  D3D12_COMMAND_QUEUE_DESC computeQueueDesc = {};
  computeQueueDesc.Flags                    = D3D12_COMMAND_QUEUE_FLAG_NONE;
  computeQueueDesc.Type                     = D3D12_COMMAND_LIST_TYPE_COMPUTE;

  VERIFY_D3D_OP(log_D3D12RenderSystem, m_device->CreateCommandQueue(&computeQueueDesc, IID_PPV_ARGS(&m_mainComputeCommandQueue)),
    "Failed to create command Queue");

  m_swapChain.Create(factory, m_mainGraphicsCommandQueue, m_window.get(), swapChainRequirements, log_D3D12RenderSystem);

  SetCurrentFrame(m_swapChain.RealComPtr()->GetCurrentBackBufferIndex());

  // Create Buffers
  m_stagingBuffer.Create(m_device, CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD), GetApplicationRequirements().m_renderer.m_stagingBufferSize,
    D3D12_RESOURCE_STATE_GENERIC_READ, log_D3D12RenderSystem);
  m_stagingBuffer.Map();


  for (const auto& shaderCreateInfo : shaderRequirements.m_shaders) {
    D3D12Renderer::AddShader(shaderCreateInfo);
  }

  for (const auto& renderTarget : renderPassRequirements.m_targets) {
    TextureDesc desc = {};
    desc.m_format    = renderTarget.m_format;
    desc.m_bounds    = Bounds3D{swapChainRequirements.m_extent.m_width, swapChainRequirements.m_extent.m_height, 1};

    if (renderTarget.m_width > 0 && renderTarget.m_height > 0 && renderTarget.m_depth > 0)
    {
      desc.m_bounds = Bounds3D{U32(renderTarget.m_width), U32(renderTarget.m_height), U32(renderTarget.m_depth)};
    }

    LOG_DBG(log_D3D12RenderSystem, LOG_LEVEL, "Creating Attachment Input at: %d for %s", m_targetImages.
      GetSize(), ToString(renderTarget.m_format).c_str());

    const D3D12_RESOURCE_STATES resourceState = D3D12_RESOURCE_STATE_COMMON;

    D3D12_RESOURCE_FLAGS resourceFlags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
    if (HasDepthComponent(desc.m_format) || HasStencilComponent(desc.m_format)) {
      resourceFlags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
    }

    m_targetImages.PushBack(D3D12ScopedImage());
    m_targetImages.Last().Create(m_device, resourceState, resourceFlags, desc, log_D3D12RenderSystem);
  }

  for (const auto& bufferTarget : renderPassRequirements.m_buffers) {
    const D3D12_RESOURCE_STATES resourceState = D3D12_RESOURCE_STATE_COMMON;
    const D3D12_RESOURCE_FLAGS resourceFlags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;

    m_targetBuffers.PushBack(D3D12ScopedBuffer());
    m_targetBuffers.Last().Create(m_device, CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT), bufferTarget.m_size, resourceState, resourceFlags, log_D3D12RenderSystem);
    m_targetBuffers.Last().SetStride(bufferTarget.m_stride);
  }

  if (swapChainRequirements.m_depthFormat != RawStorageFormat::UNKNOWN) {
    TextureDesc desc = {};
    desc.m_format = swapChainRequirements.m_depthFormat;
    desc.m_bounds = Bounds3D{ swapChainRequirements.m_extent.m_width, swapChainRequirements.m_extent.m_height, 1 };

    const D3D12_RESOURCE_STATES resourceState = D3D12_RESOURCE_STATE_DEPTH_WRITE;
    const D3D12_RESOURCE_FLAGS resourceFlags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
    m_depthTexture.Create(m_device, resourceState, resourceFlags, desc, log_D3D12RenderSystem);
  }

  m_rtvDescriptorSize = m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
  m_dsvDescriptorSize = m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_DSV);

  U32 passCount = 0;
  for (const auto& passCreateInfo : renderPassRequirements.m_passSequence) {
    if (passCreateInfo.m_type == RenderPassType::Graphics) {

      D3D12ScopedRenderPass renderPass = D3D12ScopedRenderPass(passCount, m_renderPasses.GetSize(), m_swapChain, mainAllocator,
        log_D3D12RenderSystem);

      if (passCount != (renderPassRequirements.m_passSequence.GetSize() - 1)) {
        renderPass.Create(m_device,
          passCreateInfo,
          renderPassRequirements.m_targets,
          m_targetImages,
          m_descriptorSlots,
          m_descriptorSetTable,
          m_shaders,
          m_rtvDescriptorSize, m_dsvDescriptorSize);
      }
      else {
        renderPass.CreateForSwapChain(m_device,
          passCreateInfo,
          renderPassRequirements.m_targets,
          m_targetImages,
          m_depthTexture,
          m_descriptorSlots,
          m_descriptorSetTable,
          m_shaders,
          m_rtvDescriptorSize, m_dsvDescriptorSize);
      }

      m_renderPasses.PushBack(renderPass);
      m_renderSequence.PushBack(std::make_pair(m_renderPasses.GetSize() - 1, passCreateInfo.m_type));
    }
    else if (passCreateInfo.m_type == RenderPassType::Compute)
    {
      D3D12ScopedComputePass computePass = D3D12ScopedComputePass(passCount, m_computePasses.GetSize(), mainAllocator,
        log_D3D12RenderSystem);

      computePass.Create(m_device,
        passCreateInfo,
        renderPassRequirements.m_targets,
        m_targetImages,
        m_targetBuffers,
        m_descriptorSlots,
        m_descriptorSetTable,
        m_shaders);

      m_computePasses.PushBack(computePass);
      m_renderSequence.PushBack(std::make_pair(m_computePasses.GetSize() - 1, passCreateInfo.m_type));
    }

    ++passCount;
  }
}

String D3D12Renderer::GetRenderingAPI() const {
  return "D3D12";
}

DrawablePool& D3D12Renderer::CreateDrawablePool(const DrawablePoolCreateInfo& createInfo) {
  D3D12DrawablePool pool = D3D12DrawablePool(m_device,
                                             createInfo,
                                             m_descriptorCount,
                                             m_descriptorSlots,
                                             m_shaders,
                                             m_renderPasses,
                                             m_targetBuffers,
                                             m_mainGraphicsCommandQueue,
                                             m_drawPoolAllocator,
                                             m_initAllocator,
                                             log_D3D12RenderSystem);

  m_drawablePools.PushBack(std::move(pool));

  return m_drawablePools.Last();
}

ComputePool& D3D12Renderer::CreateComputePool(const ComputePoolCreateInfo& createInfo) {
  D3D12ComputePool pool = D3D12ComputePool(m_device,
    createInfo,
    m_descriptorCount,
    m_descriptorSlots,
    m_shaders,
    m_computePasses,
    m_mainComputeCommandQueue,
    m_mainGraphicsCommandQueue,
    m_drawPoolAllocator,
    m_initAllocator,
    log_D3D12RenderSystem);

  m_computePools.PushBack(std::move(pool));

  return m_computePools.Last();
}

void D3D12Renderer::Submit() {
  LOG_DBG(log_D3D12RenderSystem, LOG_LEVEL, "Submitting Renderer");

  HEAP_ALLOCATOR(Temporary, Memory::MonotonicAllocator, 8192);

  const bool haveSubmits = m_renderTargetUpdates.GetSize() > 0 || m_bufferTargetUpdates.GetSize() > 0;

  if (haveSubmits)
  {
    auto oneTimeSubmitBuffer = D3D12ScopedCommandBuffer(m_device, D3D12_COMMAND_LIST_TYPE_DIRECT, log_D3D12RenderSystem);
    oneTimeSubmitBuffer.CreateGraphicsCommandList(m_device, nullptr, log_D3D12RenderSystem);
    auto oneTimeCommandList = oneTimeSubmitBuffer.GetGraphicsCommandList();

    LOG_DBG(log_D3D12RenderSystem, LOG_LEVEL, "Updating Render Targets with Initial Data");

    for (const auto& renderTargetUpdate : m_renderTargetUpdates)
    {
      // TODO(vasumahesh1):[STATES]: Get current state and don't hardcode
      auto& targetImage = m_targetImages[renderTargetUpdate.m_binding];
      targetImage.Transition(oneTimeCommandList, D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_COPY_DEST, log_D3D12RenderSystem);
      targetImage.CopyFromBuffer(m_device, oneTimeCommandList, m_stagingBuffer, renderTargetUpdate.m_offset);
      targetImage.Transition(oneTimeCommandList, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COMMON, log_D3D12RenderSystem);
    }

    LOG_DBG(log_D3D12RenderSystem, LOG_LEVEL, "Updating Buffer Targets with Initial Data");

    for (const auto& bufferTargetUpdate : m_bufferTargetUpdates)
    {
      auto& targetBuffer = m_targetBuffers[bufferTargetUpdate.m_binding];
      targetBuffer.Transition(oneTimeCommandList, D3D12_RESOURCE_STATE_COPY_DEST, log_D3D12RenderSystem);
      D3D12Core::CopyBuffer(oneTimeCommandList, targetBuffer, 0, m_stagingBuffer, bufferTargetUpdate.m_offset, bufferTargetUpdate.m_byteSize);
      targetBuffer.Transition(oneTimeCommandList, D3D12_RESOURCE_STATE_COMMON, log_D3D12RenderSystem);
    }

    oneTimeCommandList->Close();
    oneTimeSubmitBuffer.Execute(m_device, m_mainGraphicsCommandQueue.Get(), log_D3D12RenderSystem);
    oneTimeSubmitBuffer.WaitForComplete(m_mainGraphicsCommandQueue.Get(), log_D3D12RenderSystem);
  }

  for (auto& drawablePool : m_drawablePools) {
    drawablePool.Submit();
  }

  Vector<Vector<D3D12RenderPassRecordEntry>> renderPassEntries(m_renderPasses.GetSize(), allocatorTemporary);
  for (U32 idx = 0; idx < m_renderPasses.GetSize(); ++idx) {
    Vector<D3D12RenderPassRecordEntry> poolEntries(m_drawablePools.GetSize(), allocatorTemporary);
    renderPassEntries.PushBack(poolEntries);
  }

  U32 poolIdx = 0;
  for (auto& drawablePool : m_drawablePools) {
    Vector<std::pair<U32, D3D12RenderPassRecordEntry>> poolEntries(allocatorTemporary);
    drawablePool.GetRecordEntries(poolEntries);

    for (const auto& bufferPair : poolEntries) {
      renderPassEntries[bufferPair.first].PushBack(bufferPair.second);
      renderPassEntries[bufferPair.first].Last().m_poolIdx = poolIdx;
    }

    ++poolIdx;
  }

  for (U32 idx = 0; idx < m_renderPasses.GetSize(); ++idx) {

    const auto& renderPass = m_renderPasses[idx];

    const bool isLast = idx == m_renderPasses.GetSize() - 1;

    const U32 commandBuffers = renderPass.GetCommandBufferCount();

    for (U32 cIdx      = 0; cIdx < commandBuffers; ++cIdx) {
      auto commandList = renderPass.GetPrimaryGraphicsCommandList(cIdx);

      if (isLast) {
        renderPass.RecordImageAcquireBarrier(commandList, cIdx);
      }

      renderPass.RecordResourceBarriersForOutputsStart(commandList);
      renderPass.RecordResourceBarriersForInputsStart(commandList);

      commandList->RSSetViewports(1, &m_viewport);
      commandList->RSSetScissorRects(1, &m_scissorRect);

      renderPass.SetRenderTargets(commandList, cIdx, m_rtvDescriptorSize);

      const auto& renderPassRecords = renderPassEntries[idx];

      commandList->SetGraphicsRootSignature(renderPass.GetRootSignature());

      for (auto& recordEntry : renderPassRecords) {
        const auto& targetPool = m_drawablePools[recordEntry.m_poolIdx];

        commandList->SetPipelineState(recordEntry.m_pso);

        const auto& allHeaps = targetPool.GetAllDescriptorHeaps();
        commandList->SetDescriptorHeaps(UINT(allHeaps.GetSize()), allHeaps.Data());

        commandList->ExecuteBundle(recordEntry.m_bundle);
      }

      renderPass.RecordResourceBarriersForOutputsEnd(commandList);
      renderPass.RecordResourceBarriersForInputsEnd(commandList);

      if (isLast) {
        renderPass.RecordPresentBarrier(commandList, cIdx);
      }

      commandList->Close();
    }
  }


  // COMPUTE SUBMIT

  for (auto& computePool : m_computePools) {
    computePool.Submit();
  }

  // Begin Recording - Open all command buffers
  for (U32 idx = 0; idx < m_computePasses.GetSize(); ++idx) {
    auto& computePass = m_computePasses[idx];

    LOG_DBG(log_D3D12RenderSystem, LOG_LEVEL, "Begin Recording Compute Pass: %d", idx);

    const auto commandList = computePass.GetPrimaryComputeCommandList(0);

    computePass.RecordResourceBarriersForOutputsStart(commandList);
    computePass.RecordResourceBarriersForInputsStart(commandList);

    // Record Pool Wise - Pass Wise data
    for (auto& computePool : m_computePools) {
      computePool.Record(computePass.GetId());
    }

    computePass.RecordResourceBarriersForOutputsEnd(commandList);
    computePass.RecordResourceBarriersForInputsEnd(commandList);
    commandList->Close();

    LOG_DBG(log_D3D12RenderSystem, LOG_LEVEL, "End Recording Compute Pass: %d", idx);
  }

  LOG_DBG(log_D3D12RenderSystem, LOG_LEVEL, "Submit Complete - All Render Passes recorded");
}

void D3D12Renderer::RenderFrame() {
  EnterRenderFrame();

  const auto& currentFrame = GetCurrentFrame();

  U32 count = 0;
  for(const auto& pass : m_renderSequence)
  {
    if (count == m_renderSequence.GetSize() - 1)
    {
      // Don't do last render pass
      break;
    }

    const auto idx = pass.first;

    if (pass.second == RenderPassType::Graphics)
    {
      std::array<ID3D12CommandList*, 1> list = { m_renderPasses[idx].GetPrimaryGraphicsCommandList(0) };
      m_mainGraphicsCommandQueue->ExecuteCommandLists(UINT(list.size()), list.data());
      m_renderPasses[idx].WaitForGPU(m_mainGraphicsCommandQueue.Get());
    }
    else if (pass.second == RenderPassType::Compute)
    {
      std::array<ID3D12CommandList*, 1> list = { m_computePasses[idx].GetPrimaryComputeCommandList(0) };
      m_mainComputeCommandQueue->ExecuteCommandLists(UINT(list.size()), list.data());
      m_computePasses[idx].WaitForGPU(m_mainComputeCommandQueue.Get());
    }

    ++count;
  }

  std::array<ID3D12CommandList*, 1> swapChainList = {m_renderPasses.Last().GetPrimaryGraphicsCommandList(currentFrame)};
  m_mainGraphicsCommandQueue->ExecuteCommandLists(UINT(swapChainList.size()), swapChainList.data()); // NOLINT

  // Present and update the frame index for the next frame.
  VERIFY_D3D_OP(log_D3D12RenderSystem, m_swapChain.RealComPtr()->Present(1, 0), "Present Failed");

  m_renderPasses.Last().WaitForGPU(m_mainGraphicsCommandQueue.Get());

  m_perFrameAllocator.Reset();
  ExitRenderFrame();
}

void D3D12Renderer::SnapshotFrame(const String& exportPath) const {
  LOG_DBG(log_D3D12RenderSystem, LOG_LEVEL, "Exporting Screenshot: %s", exportPath.c_str());
  UNUSED(exportPath);
}

void D3D12Renderer::AddShader(const ShaderCreateInfo& info) {
  const String fullPath = "Shaders/" + D3D12Renderer::GetRenderingAPI() + "/" + info.m_shaderFileName;

  D3D12ScopedShader shader = D3D12ScopedShader(fullPath, m_initAllocator, log_D3D12RenderSystem);
  m_shaders.PushBack(std::move(shader));

  m_shaders.Last().SetStage(info.m_stage);
}

void D3D12Renderer::BindRenderTarget(U32 renderTargetId, const TextureDesc& desc, const U8* buffer) {
  LOG_DBG(log_D3D12RenderSystem, LOG_LEVEL,
    "D3D12 Render Target: Updating Render Target: %d of Size: %d bytes", renderTargetId, desc.m_size);

  const U32 size = 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_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         = renderTargetId;
  info.m_set             = 0;

  m_renderTargetUpdates.PushBack(info);
}

void D3D12Renderer::BindBufferTarget(U32 bufferTargetId, const U8* buffer) {
  const U32 size = m_targetBuffers[bufferTargetId].GetSize();

  LOG_DBG(log_D3D12RenderSystem, LOG_LEVEL,
    "D3D12 Render Target: Updating Buffer Target: %d of Size: %d bytes", bufferTargetId, size);

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

  BufferTargetInfo info = BufferTargetInfo();
  info.m_byteSize        = size;
  info.m_offset          = offset;
  info.m_binding         = bufferTargetId;
  info.m_set             = 0;

  m_bufferTargetUpdates.PushBack(info);
}

} // namespace D3D12
} // namespace Azura