/* This file is generated by vkr_device_object.py. */

#ifndef VKR_PIPELINE_GEN_H
#define VKR_PIPELINE_GEN_H

#include "vkr_common.h"

#include "venus-protocol/vn_protocol_renderer_shader_module.h"
#include "venus-protocol/vn_protocol_renderer_pipeline_layout.h"
#include "venus-protocol/vn_protocol_renderer_pipeline_cache.h"
#include "venus-protocol/vn_protocol_renderer_pipeline.h"

#include "vkr_context.h"
#include "vkr_device.h"

/* create a driver VkShaderModule and update the vkr_shader_module */
static inline VkResult
vkr_shader_module_create_driver_handle(
   UNUSED struct vkr_context *ctx,
   struct vn_command_vkCreateShaderModule *args,
   struct vkr_shader_module *obj)
{
   struct vkr_device *dev = vkr_device_from_handle(args->device);
   struct vn_device_proc_table *vk = &dev->proc_table;

   /* handles in args are replaced */
   vn_replace_vkCreateShaderModule_args_handle(args);
   args->ret = vk->CreateShaderModule(args->device, args->pCreateInfo, NULL,
      &obj->base.handle.shader_module);
   return args->ret;
}

/* create a vkr_shader_module */
static inline struct vkr_shader_module *
vkr_shader_module_create(
   struct vkr_context *ctx,
   struct vn_command_vkCreateShaderModule *args)
{
   struct vkr_shader_module *obj = vkr_context_alloc_object(ctx, sizeof(*obj),
      VK_OBJECT_TYPE_SHADER_MODULE, args->pShaderModule);
   if (!obj) {
      args->ret = VK_ERROR_OUT_OF_HOST_MEMORY;
      return NULL;
   }

   /* handles in args are replaced */
   if (vkr_shader_module_create_driver_handle(ctx, args, obj) != VK_SUCCESS) {
      free(obj);
      return NULL;
   }

   return obj;
}

/* create a vkr_shader_module and add it to the vkr_device */
static inline struct vkr_shader_module *
vkr_shader_module_create_and_add(
   struct vkr_context *ctx,
   struct vn_command_vkCreateShaderModule *args)
{
   struct vkr_device *dev = vkr_device_from_handle(args->device);

   struct vkr_shader_module *obj = vkr_shader_module_create(ctx, args);
   if (!obj)
      return NULL;

   vkr_device_add_object(ctx, dev, &obj->base);
   return obj;
}

/* destroy a driver VkShaderModule */
static inline void
vkr_shader_module_destroy_driver_handle(
   UNUSED struct vkr_context *ctx,
   struct vn_command_vkDestroyShaderModule *args)
{
   struct vkr_device *dev = vkr_device_from_handle(args->device);
   struct vn_device_proc_table *vk = &dev->proc_table;

   /* handles in args are replaced */
   vn_replace_vkDestroyShaderModule_args_handle(args);
   vk->DestroyShaderModule(args->device, args->shaderModule, NULL);
}

/* remove a vkr_shader_module from the device and destroy it */
static inline void
vkr_shader_module_destroy_and_remove(
   struct vkr_context *ctx,
   struct vn_command_vkDestroyShaderModule *args)
{
   struct vkr_device *dev = vkr_device_from_handle(args->device);
   struct vkr_shader_module *obj = vkr_shader_module_from_handle(args->shaderModule);
   if (!obj)
      return;

   vkr_shader_module_destroy_driver_handle(ctx, args);

   vkr_device_remove_object(ctx, dev, &obj->base);
}

/* create a driver VkPipelineLayout and update the vkr_pipeline_layout */
static inline VkResult
vkr_pipeline_layout_create_driver_handle(
   UNUSED struct vkr_context *ctx,
   struct vn_command_vkCreatePipelineLayout *args,
   struct vkr_pipeline_layout *obj)
{
   struct vkr_device *dev = vkr_device_from_handle(args->device);
   struct vn_device_proc_table *vk = &dev->proc_table;

   /* handles in args are replaced */
   vn_replace_vkCreatePipelineLayout_args_handle(args);
   args->ret = vk->CreatePipelineLayout(args->device, args->pCreateInfo, NULL,
      &obj->base.handle.pipeline_layout);
   return args->ret;
}

/* create a vkr_pipeline_layout */
static inline struct vkr_pipeline_layout *
vkr_pipeline_layout_create(
   struct vkr_context *ctx,
   struct vn_command_vkCreatePipelineLayout *args)
{
   struct vkr_pipeline_layout *obj = vkr_context_alloc_object(ctx, sizeof(*obj),
      VK_OBJECT_TYPE_PIPELINE_LAYOUT, args->pPipelineLayout);
   if (!obj) {
      args->ret = VK_ERROR_OUT_OF_HOST_MEMORY;
      return NULL;
   }

   /* handles in args are replaced */
   if (vkr_pipeline_layout_create_driver_handle(ctx, args, obj) != VK_SUCCESS) {
      free(obj);
      return NULL;
   }

   return obj;
}

/* create a vkr_pipeline_layout and add it to the vkr_device */
static inline struct vkr_pipeline_layout *
vkr_pipeline_layout_create_and_add(
   struct vkr_context *ctx,
   struct vn_command_vkCreatePipelineLayout *args)
{
   struct vkr_device *dev = vkr_device_from_handle(args->device);

   struct vkr_pipeline_layout *obj = vkr_pipeline_layout_create(ctx, args);
   if (!obj)
      return NULL;

   vkr_device_add_object(ctx, dev, &obj->base);
   return obj;
}

/* destroy a driver VkPipelineLayout */
static inline void
vkr_pipeline_layout_destroy_driver_handle(
   UNUSED struct vkr_context *ctx,
   struct vn_command_vkDestroyPipelineLayout *args)
{
   struct vkr_device *dev = vkr_device_from_handle(args->device);
   struct vn_device_proc_table *vk = &dev->proc_table;

   /* handles in args are replaced */
   vn_replace_vkDestroyPipelineLayout_args_handle(args);
   vk->DestroyPipelineLayout(args->device, args->pipelineLayout, NULL);
}

/* remove a vkr_pipeline_layout from the device and destroy it */
static inline void
vkr_pipeline_layout_destroy_and_remove(
   struct vkr_context *ctx,
   struct vn_command_vkDestroyPipelineLayout *args)
{
   struct vkr_device *dev = vkr_device_from_handle(args->device);
   struct vkr_pipeline_layout *obj = vkr_pipeline_layout_from_handle(args->pipelineLayout);
   if (!obj)
      return;

   vkr_pipeline_layout_destroy_driver_handle(ctx, args);

   vkr_device_remove_object(ctx, dev, &obj->base);
}

/* create a driver VkPipelineCache and update the vkr_pipeline_cache */
static inline VkResult
vkr_pipeline_cache_create_driver_handle(
   UNUSED struct vkr_context *ctx,
   struct vn_command_vkCreatePipelineCache *args,
   struct vkr_pipeline_cache *obj)
{
   struct vkr_device *dev = vkr_device_from_handle(args->device);
   struct vn_device_proc_table *vk = &dev->proc_table;

   /* handles in args are replaced */
   vn_replace_vkCreatePipelineCache_args_handle(args);
   args->ret = vk->CreatePipelineCache(args->device, args->pCreateInfo, NULL,
      &obj->base.handle.pipeline_cache);
   return args->ret;
}

/* create a vkr_pipeline_cache */
static inline struct vkr_pipeline_cache *
vkr_pipeline_cache_create(
   struct vkr_context *ctx,
   struct vn_command_vkCreatePipelineCache *args)
{
   struct vkr_pipeline_cache *obj = vkr_context_alloc_object(ctx, sizeof(*obj),
      VK_OBJECT_TYPE_PIPELINE_CACHE, args->pPipelineCache);
   if (!obj) {
      args->ret = VK_ERROR_OUT_OF_HOST_MEMORY;
      return NULL;
   }

   /* handles in args are replaced */
   if (vkr_pipeline_cache_create_driver_handle(ctx, args, obj) != VK_SUCCESS) {
      free(obj);
      return NULL;
   }

   return obj;
}

/* create a vkr_pipeline_cache and add it to the vkr_device */
static inline struct vkr_pipeline_cache *
vkr_pipeline_cache_create_and_add(
   struct vkr_context *ctx,
   struct vn_command_vkCreatePipelineCache *args)
{
   struct vkr_device *dev = vkr_device_from_handle(args->device);

   struct vkr_pipeline_cache *obj = vkr_pipeline_cache_create(ctx, args);
   if (!obj)
      return NULL;

   vkr_device_add_object(ctx, dev, &obj->base);
   return obj;
}

/* destroy a driver VkPipelineCache */
static inline void
vkr_pipeline_cache_destroy_driver_handle(
   UNUSED struct vkr_context *ctx,
   struct vn_command_vkDestroyPipelineCache *args)
{
   struct vkr_device *dev = vkr_device_from_handle(args->device);
   struct vn_device_proc_table *vk = &dev->proc_table;

   /* handles in args are replaced */
   vn_replace_vkDestroyPipelineCache_args_handle(args);
   vk->DestroyPipelineCache(args->device, args->pipelineCache, NULL);
}

/* remove a vkr_pipeline_cache from the device and destroy it */
static inline void
vkr_pipeline_cache_destroy_and_remove(
   struct vkr_context *ctx,
   struct vn_command_vkDestroyPipelineCache *args)
{
   struct vkr_device *dev = vkr_device_from_handle(args->device);
   struct vkr_pipeline_cache *obj = vkr_pipeline_cache_from_handle(args->pipelineCache);
   if (!obj)
      return;

   vkr_pipeline_cache_destroy_driver_handle(ctx, args);

   vkr_device_remove_object(ctx, dev, &obj->base);
}

/* initialize an object_array for vkr_pipelines */
static inline VkResult
vkr_graphics_pipeline_init_array(
   struct vkr_context *ctx,
   struct vn_command_vkCreateGraphicsPipelines *args,
   struct object_array *arr)
{
   args->ret = object_array_init(ctx, arr, args->createInfoCount,
                                 VK_OBJECT_TYPE_PIPELINE, sizeof(struct vkr_pipeline),
                                 sizeof(*args->pPipelines),
                                 args->pPipelines)
                  ? VK_SUCCESS
                  : VK_ERROR_OUT_OF_HOST_MEMORY;
   return args->ret;
}

/* create an array of driver VkPipelines and update the object_array */
static inline VkResult
vkr_graphics_pipeline_create_driver_handles(
   UNUSED struct vkr_context *ctx,
   struct vn_command_vkCreateGraphicsPipelines *args,
   struct object_array *arr)
{
   struct vkr_device *dev = vkr_device_from_handle(args->device);
   struct vn_device_proc_table *vk = &dev->proc_table;

   /* handles in args are replaced */
   vn_replace_vkCreateGraphicsPipelines_args_handle(args);
   args->ret = vk->CreateGraphicsPipelines(args->device, args->pipelineCache,
      args->createInfoCount, args->pCreateInfos, NULL,
      arr->handle_storage);
   return args->ret;
}

/* create an array of vkr_pipelines */
static inline VkResult
vkr_graphics_pipeline_create_array(
   struct vkr_context *ctx,
   struct vn_command_vkCreateGraphicsPipelines *args,
   struct object_array *arr)
{
   if (vkr_graphics_pipeline_init_array(ctx, args, arr) != VK_SUCCESS)
      return args->ret;

   if (vkr_graphics_pipeline_create_driver_handles(ctx, args, arr) < VK_SUCCESS) {
      /* In case the client expects a reply, clear all returned handles to
       * VK_NULL_HANDLE.
       */
      memset(args->pPipelines, 0,
             args->createInfoCount * sizeof(args->pPipelines[0]));
      object_array_fini(arr);
      return args->ret;
   }

   return args->ret;
}

/* steal vkr_pipelines from an object_array and add them to the device */
static inline void
vkr_pipeline_add_array(
   struct vkr_context *ctx,
   struct vkr_device *dev,
   struct object_array *arr,
   VkPipeline *args_pPipelines)
{
   for (uint32_t i = 0; i < arr->count; i++) {
      struct vkr_pipeline *obj = arr->objects[i];

      obj->base.handle.pipeline = ((VkPipeline *)arr->handle_storage)[i];

      /* Individual pipelines may fail creation. */
      if (obj->base.handle.pipeline == VK_NULL_HANDLE) {
         free(obj);
         arr->objects[i] = NULL;
         args_pPipelines[i] = VK_NULL_HANDLE;
      } else {
         vkr_device_add_object(ctx, dev, &obj->base);
      }
   }

   arr->objects_stolen = true;
   object_array_fini(arr);
}

/* destroy a driver VkPipeline */
static inline void
vkr_pipeline_destroy_driver_handle(
   UNUSED struct vkr_context *ctx,
   struct vn_command_vkDestroyPipeline *args)
{
   struct vkr_device *dev = vkr_device_from_handle(args->device);
   struct vn_device_proc_table *vk = &dev->proc_table;

   /* handles in args are replaced */
   vn_replace_vkDestroyPipeline_args_handle(args);
   vk->DestroyPipeline(args->device, args->pipeline, NULL);
}

/* remove a vkr_pipeline from the device and destroy it */
static inline void
vkr_pipeline_destroy_and_remove(
   struct vkr_context *ctx,
   struct vn_command_vkDestroyPipeline *args)
{
   struct vkr_device *dev = vkr_device_from_handle(args->device);
   struct vkr_pipeline *obj = vkr_pipeline_from_handle(args->pipeline);
   if (!obj)
      return;

   vkr_pipeline_destroy_driver_handle(ctx, args);

   vkr_device_remove_object(ctx, dev, &obj->base);
}

/* initialize an object_array for vkr_pipelines */
static inline VkResult
vkr_compute_pipeline_init_array(
   struct vkr_context *ctx,
   struct vn_command_vkCreateComputePipelines *args,
   struct object_array *arr)
{
   args->ret = object_array_init(ctx, arr, args->createInfoCount,
                                 VK_OBJECT_TYPE_PIPELINE, sizeof(struct vkr_pipeline),
                                 sizeof(*args->pPipelines),
                                 args->pPipelines)
                  ? VK_SUCCESS
                  : VK_ERROR_OUT_OF_HOST_MEMORY;
   return args->ret;
}

/* create an array of driver VkPipelines and update the object_array */
static inline VkResult
vkr_compute_pipeline_create_driver_handles(
   UNUSED struct vkr_context *ctx,
   struct vn_command_vkCreateComputePipelines *args,
   struct object_array *arr)
{
   struct vkr_device *dev = vkr_device_from_handle(args->device);
   struct vn_device_proc_table *vk = &dev->proc_table;

   /* handles in args are replaced */
   vn_replace_vkCreateComputePipelines_args_handle(args);
   args->ret = vk->CreateComputePipelines(args->device, args->pipelineCache,
      args->createInfoCount, args->pCreateInfos, NULL,
      arr->handle_storage);
   return args->ret;
}

/* create an array of vkr_pipelines */
static inline VkResult
vkr_compute_pipeline_create_array(
   struct vkr_context *ctx,
   struct vn_command_vkCreateComputePipelines *args,
   struct object_array *arr)
{
   if (vkr_compute_pipeline_init_array(ctx, args, arr) != VK_SUCCESS)
      return args->ret;

   if (vkr_compute_pipeline_create_driver_handles(ctx, args, arr) < VK_SUCCESS) {
      /* In case the client expects a reply, clear all returned handles to
       * VK_NULL_HANDLE.
       */
      memset(args->pPipelines, 0,
             args->createInfoCount * sizeof(args->pPipelines[0]));
      object_array_fini(arr);
      return args->ret;
   }

   return args->ret;
}

#endif /* VKR_PIPELINE_GEN_H */
