/*
 * gulkan
 * Copyright 2018 Collabora Ltd.
 * Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com>
 * SPDX-License-Identifier: MIT
 */

#include "gulkan-uniform-buffer.h"

struct _GulkanUniformBuffer
{
  GObject parent;

  VkBuffer buffer;
  VkDeviceMemory memory;
  VkDevice device;
  VkDeviceSize size;
  void *data;
};

G_DEFINE_TYPE (GulkanUniformBuffer, gulkan_uniform_buffer, G_TYPE_OBJECT)

static void
gulkan_uniform_buffer_finalize (GObject *gobject);

static void
gulkan_uniform_buffer_class_init (GulkanUniformBufferClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  object_class->finalize = gulkan_uniform_buffer_finalize;
}

static void
gulkan_uniform_buffer_init (GulkanUniformBuffer *self)
{
  self->buffer = VK_NULL_HANDLE;
  self->memory = VK_NULL_HANDLE;
}

GulkanUniformBuffer *
gulkan_uniform_buffer_new (void)
{
  return (GulkanUniformBuffer*) g_object_new (GULKAN_TYPE_UNIFORM_BUFFER, 0);
}

bool
gulkan_uniform_buffer_allocate_and_map (GulkanUniformBuffer *self,
                                        GulkanDevice        *device,
                                        VkDeviceSize         size)
{
  self->device = gulkan_device_get_handle (device);
  self->size = size;

  if (!gulkan_device_create_buffer (device,
                                    size,
                                    VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
                                    VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
                                    VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
                                    VK_MEMORY_PROPERTY_HOST_CACHED_BIT,
                                   &self->buffer,
                                   &self->memory))
    {
      g_printerr ("Could not create buffer\n");
      return FALSE;
    }

  vkMapMemory (self->device, self->memory, 0, VK_WHOLE_SIZE, 0, &self->data);
  return TRUE;
}

static void
gulkan_uniform_buffer_finalize (GObject *gobject)
{
  GulkanUniformBuffer *self = GULKAN_UNIFORM_BUFFER (gobject);

  vkDestroyBuffer (self->device, self->buffer, NULL);
  vkFreeMemory (self->device, self->memory, NULL);
}

void
gulkan_uniform_buffer_update_matrix (GulkanUniformBuffer *self,
                                     graphene_matrix_t *matrix)
{
  graphene_matrix_to_float (matrix, self->data);
}

void
gulkan_uniform_buffer_update_vec4 (GulkanUniformBuffer   *self,
                                   const graphene_vec4_t *v)
{
  graphene_vec4_to_float (v, self->data);
}

void
gulkan_uniform_buffer_update_struct (GulkanUniformBuffer *self,
                                     gpointer            *s)
{
  memcpy (self->data, s, self->size);
}

VkBuffer
gulkan_uniform_buffer_get_handle (GulkanUniformBuffer *self)
{
  return self->buffer;
}
