Newer
Older
ForwardPlusRenderer / src / Buffer / Buffer.cpp
#include <stdafx.h>
#include "Buffer/Buffer.h"
#include "Context.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;
}

uint32_t FindMemoryTypeIndex(
    vk::PhysicalDevice      physical_device,
    uint32_t                type_field,
    vk::MemoryPropertyFlags type_flags)
{
  vk::PhysicalDeviceMemoryProperties device_mem_properties = physical_device.getMemoryProperties();

  for(uint32_t index = 0; index < device_mem_properties.memoryTypeCount; ++index)
  {
    bool memory_type_index_found = IsNthBitSet(type_field, index) &&
        IsOnlyNthBitSet(static_cast<uint32_t>(device_mem_properties.memoryTypes[index].propertyFlags),
                        static_cast<uint32_t>(type_flags));
    if(memory_type_index_found)
      return index;
  }
  return std::numeric_limits<uint32_t>::max();
}

bool IsNthBitSet(uint32_t bit_field, uint32_t nth_bit) FPR_NOEXCEPT
{
  return (bit_field & (1 << nth_bit));
};

bool IsOnlyNthBitSet(uint32_t bit_field, uint32_t nth_bit) FPR_NOEXCEPT
{
  return (bit_field & nth_bit) == nth_bit;
}


vk::UniqueDeviceMemory CreateImageMemory(fpr::Device* device, vk::Image image)
{
  vk::MemoryRequirements mem_req = device->GetLogicalDeviceHandle().getImageMemoryRequirements(image);

  vk::MemoryAllocateInfo mem_alloc_info{};
  mem_alloc_info.allocationSize  = mem_req.size;
  mem_alloc_info.memoryTypeIndex = fpr::FindMemoryTypeIndex(
      device->GetPhysicalDeviceHandle(), mem_req.memoryTypeBits, vk::MemoryPropertyFlagBits::eDeviceLocal);
  auto [alloc_result, img_mem] = device->GetLogicalDeviceHandle().allocateMemoryUnique(mem_alloc_info);
  assert(("Failed to allocate image memory!", alloc_result == vk::Result::eSuccess));
  [[maybe_unused]] vk::Result bind_result = device->GetLogicalDeviceHandle().bindImageMemory(image, *img_mem, 0);

  assert(("Failed to bind image memory!", bind_result == vk::Result::eSuccess));

  return std::move(img_mem);
}

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