/**
 *
 * @file src/render/vulkan/Vk_buffer.cpp
 *
 * @copyright 2008-2024 Bordeaux INP, CNRS (LaBRI UMR 5800), Inria,
 *                      Univ. Bordeaux. All rights reserved.
 *
 * @author Camille Ordronneau
 * @author Johnny Jazeix
 * @author Lucas Guedon
 *
 * @date 2024-07-17
 */
#include "render/vulkan/Vk_buffer.hpp"

Vk_buffer::Vk_buffer(VkBufferUsageFlagBits flags) :
    _flags(flags) { }

Vk_buffer::~Vk_buffer() {
    free_buffer();
}

void Vk_buffer::init(VkDevice dev, QVulkanDeviceFunctions *dev_funcs, uint32_t memory_index) {
    _dev = dev;
    _dev_funcs = dev_funcs;
    _memory_index = memory_index;
}

void Vk_buffer::alloc_buffer(VkDeviceSize size) {
    if (size == 0)
        size = 1;
    // Initialize buffer information for its creation
    VkBufferCreateInfo buf_info = {
        .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
        .size = size,
        .usage = _flags
    };

    VkResult err = _dev_funcs->vkCreateBuffer(_dev, &buf_info, nullptr, &_buffer);
    if (err != VK_SUCCESS)
        qFatal("Failed to create buffer: %d", err);

    // Initialize the memory needed by the buffer
    VkMemoryRequirements mem_requirements;
    _dev_funcs->vkGetBufferMemoryRequirements(_dev, _buffer, &mem_requirements);

    // Allocate the memory needed
    VkMemoryAllocateInfo allocation_info = {
        .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
        .pNext = nullptr,
        .allocationSize = mem_requirements.size,
        .memoryTypeIndex = _memory_index
    };

    err = _dev_funcs->vkAllocateMemory(_dev, &allocation_info, nullptr, &_buf_mem);
    if (err != VK_SUCCESS)
        qFatal("Failed to allocate memory: %d", err);

    // Bind the allocated memory to the buffer
    err = _dev_funcs->vkBindBufferMemory(_dev, _buffer, _buf_mem, 0);
    if (err != VK_SUCCESS)
        qFatal("Failed to bind buffer memory: %d", err);

    _mem_size = mem_requirements.size;
}

void Vk_buffer::free_buffer() {
    if (_buffer) {
        _dev_funcs->vkDestroyBuffer(_dev, _buffer, nullptr);
        _buffer = VK_NULL_HANDLE;
    }
    if (_buf_mem) {
        _dev_funcs->vkFreeMemory(_dev, _buf_mem, nullptr);
        _buf_mem = VK_NULL_HANDLE;
    }
}

void Vk_buffer::realloc_buffer(VkDeviceSize new_size) {
    free_buffer();
    alloc_buffer(new_size);
}

VkDeviceSize Vk_buffer::get_mem_size() {
    return _mem_size;
}