/**
 *
 * @file src/render/vulkan/Vk_uniform_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_uniform_buffer.hpp"

Vk_uniform_buffer::Vk_uniform_buffer::Vk_uniform_buffer() :
    Vk_buffer(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) { }

void Vk_uniform_buffer::init_uniform(const VkDescriptorSetAllocateInfo *descriptor_info, uint32_t concurrent_frame_count, VkDeviceSize alloc_size) {
    alloc_buffer(concurrent_frame_count * alloc_size);

    VkDeviceSize mem_size_uniform = get_mem_size();
    quint8 *buffer_data;
    VkResult err = _dev_funcs->vkMapMemory(_dev, _buf_mem, 0, mem_size_uniform, 0, reinterpret_cast<void **>(&buffer_data));
    if (err != VK_SUCCESS)
        qFatal("Failed to map memory: %d", err);

    // Initialize uniform data
    QMatrix4x4 ident;
    memset(_uniform_buf_info, 0, sizeof(_uniform_buf_info));
    for (uint32_t frame_index = 0; frame_index < concurrent_frame_count; ++frame_index) {
        const VkDeviceSize offset = frame_index * alloc_size;
        memcpy(buffer_data + offset, ident.constData(), 16 * sizeof(float));
        memcpy(buffer_data + offset + 16 * sizeof(float), ident.constData(), 16 * sizeof(float));
        _uniform_buf_info[frame_index].buffer = _buffer;
        _uniform_buf_info[frame_index].offset = offset;
        _uniform_buf_info[frame_index].range = alloc_size;
    }
    _dev_funcs->vkUnmapMemory(_dev, _buf_mem);

    // Allocate resources for each frame
    for (uint32_t frame_index = 0; frame_index < concurrent_frame_count; ++frame_index) {

        err = _dev_funcs->vkAllocateDescriptorSets(_dev, descriptor_info, &_desc_set[frame_index]);
        if (err != VK_SUCCESS)
            qFatal("Failed to allocate descriptor set: %d", err);

        VkWriteDescriptorSet desc_write = {
            .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
            .dstSet = _desc_set[frame_index],
            .descriptorCount = 1,
            .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
            .pBufferInfo = &_uniform_buf_info[frame_index]
        };
        _dev_funcs->vkUpdateDescriptorSets(_dev, 1, &desc_write, 0, nullptr);
    }
}

void Vk_uniform_buffer::set_variable(uint32_t frame_index, VkDeviceSize offset, VkDeviceSize size, const void *value) {
    quint8 *buffer_data;
    VkResult err = _dev_funcs->vkMapMemory(_dev, _buf_mem, _uniform_buf_info[frame_index].offset + offset, size, 0, reinterpret_cast<void **>(&buffer_data));
    if (err != VK_SUCCESS)
        qFatal("Failed to map memory: %d", err);

    memcpy(buffer_data, value, size);
    _dev_funcs->vkUnmapMemory(_dev, _buf_mem);
}

void Vk_uniform_buffer::bind_uniform_buffer(VkCommandBuffer &command_buffer, uint32_t frame_index, VkPipelineLayout &pipeline_layout) {
    _dev_funcs->vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &_desc_set[frame_index], 0, nullptr);
}
