Newer
Older
ForwardPlusRenderer / src / Buffer / Buffer.cpp
#include <stdafx.h>
#include "Buffer/Buffer.h"
#include "Context.h"
#include "Memory.h"

namespace fpr
{
Buffer::Buffer(vk::DeviceSize buffer_size, vk::BufferUsageFlags usage_flags, vk::MemoryPropertyFlags mem_flags):
    m_buffer_size(buffer_size), m_usage(usage_flags), m_memory_flags(mem_flags)
{
  auto&                context = fpr::Context::Get();
  auto&                device  = context.GetDevice()->GetLogicalDeviceHandle();
  vk::BufferCreateInfo buffer_create_info{};
  buffer_create_info.size        = buffer_size;
  buffer_create_info.usage       = usage_flags;
  buffer_create_info.sharingMode = vk::SharingMode::eExclusive;

  auto [buffer_result, buffer] = device.createBufferUnique(buffer_create_info);

  assert(("Failed to create buffer.", buffer_result == vk::Result::eSuccess));

  vk::MemoryRequirements mem_req = device.getBufferMemoryRequirements(*buffer);
  vk::MemoryAllocateInfo mem_alloc_info{};
  const uint32_t         mem_type_index =
      FindMemoryTypeIndex(context.GetDevice()->GetPhysicalDeviceHandle(), mem_req.memoryTypeBits, mem_flags);

  [[maybe_unused]] bool found = mem_type_index != std::numeric_limits<uint32_t>::max();
  assert(("Memory type index not found.", found));

  mem_alloc_info.allocationSize = mem_req.size;

  mem_alloc_info.memoryTypeIndex = mem_type_index;

  auto [alloc_result, buffer_memory] = device.allocateMemoryUnique(mem_alloc_info);
  assert(("Failed to allocate buffer memory.", alloc_result == vk::Result::eSuccess));
  m_buffer = std::move(buffer);
  m_memory = std::move(buffer_memory);

  [[maybe_unused]] vk::Result bind_result = device.bindBufferMemory(*m_buffer, *m_memory, 0);

  assert(("Failed to bind buffer memory.", bind_result == vk::Result::eSuccess));
}

Buffer::~Buffer()
{
  // Most buffers are constructed and permanently mapped. Therefore, check if they are still mapped upon destructiion.
  if(mapped_data)
    Flush();
}

vk::DeviceSize Buffer::GetSize()
{
  return m_buffer_size;
}

vk::Buffer& Buffer::GetBuffer()
{
  return *m_buffer;
}

void Buffer::Transfer(Buffer* other)
{
  vk::CommandBuffer& transfer_cmd_buff = fpr::Context::Get().BeginSingleTimeCommands();

  vk::BufferCopy copy_info;
  copy_info.size = GetSize();
  transfer_cmd_buff.copyBuffer(GetBuffer(), other->GetBuffer(), copy_info);
  fpr::Context::Get().EndSingleTimeCommands();
}

void Buffer::Update(const void* new_data)
{
  void* mapped = GetMapped();
  memcpy(mapped, new_data, m_buffer_size);

  Flush();
}

void Buffer::Flush()
{
  vk::MappedMemoryRange range;
  range.memory = *m_memory;
  range.size   = m_buffer_size;
  [[maybe_unused]] vk::Result flush_result =
      fpr::Context::Get().GetDevice()->GetLogicalDeviceHandle().flushMappedMemoryRanges(range);

  assert(("Failed to flush mapped memory.", flush_result == vk::Result::eSuccess));
}

void* Buffer::GetMapped()
{
  // Get mapped memory. If not mapped, map it and return pointer to memory.
  if(!mapped_data) [[unlikely]]
  {
    vk::Result map_result;

    std::tie(map_result, mapped_data) =
        fpr::Context::Get().GetDevice()->GetLogicalDeviceHandle().mapMemory(*m_memory, 0, m_buffer_size);

    assert(("Failed to map memory.", map_result == vk::Result::eSuccess));
  }

  return mapped_data;
}

} // namespace fpr