/*
 * Copyright © 2025 Erico Nunes
 *
 * based on gl-renderer-internal.h:
 * Copyright © 2019 Collabora, Ltd.
 * Copyright © 2019 Harish Krupo
 * Copyright © 2019 Intel Corporation
 * Copyright 2021 Advanced Micro Devices, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice (including the
 * next paragraph) shall be included in all copies or substantial
 * portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#ifndef VULKAN_RENDERER_INTERNAL_H
#define VULKAN_RENDERER_INTERNAL_H

#include <stdbool.h>
#include <time.h>

#include <vulkan/vulkan.h>
#include "shared/helpers.h"
#include "libweston/libweston.h"
#include "libweston/libweston-internal.h"

#if defined(BUILD_WAYLAND_COMPOSITOR)
#define VK_USE_PLATFORM_WAYLAND_KHR
#include <vulkan/vulkan_wayland.h>
#endif

#if defined(BUILD_X11_COMPOSITOR)
#define VK_USE_PLATFORM_XCB_KHR
#include <xcb/xcb.h>
#include <vulkan/vulkan_xcb.h>
#endif

#define MAX_NUM_IMAGES 5
#define MAX_CONCURRENT_FRAMES 2

struct vulkan_extension_table {
	const char *name;
	uint64_t flag;
	uint64_t instance_dep;
	uint64_t device_dep;
};

/* Keep in sync with vulkan-renderer.c. */
enum vulkan_inst_ext_flag {
	EXTENSION_KHR_EXTERNAL_MEMORY_CAPABILITIES       = 1ull <<  0,
	EXTENSION_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES    = 1ull <<  1,
	EXTENSION_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2   = 1ull <<  2,
	EXTENSION_KHR_SURFACE                            = 1ull <<  3,
	EXTENSION_KHR_WAYLAND_SURFACE                    = 1ull <<  4,
	EXTENSION_KHR_XCB_SURFACE                        = 1ull <<  5,
};

/* Keep in sync with vulkan-renderer.c. */
enum vulkan_device_ext_flag {
	EXTENSION_EXT_EXTERNAL_MEMORY_DMA_BUF            = 1ull <<  0,
	EXTENSION_EXT_IMAGE_DRM_FORMAT_MODIFIER          = 1ull <<  1,
	EXTENSION_EXT_PHYSICAL_DEVICE_DRM                = 1ull <<  2,
	EXTENSION_EXT_QUEUE_FAMILY_FOREIGN               = 1ull <<  3,
	EXTENSION_KHR_BIND_MEMORY_2                      = 1ull <<  4,
	EXTENSION_KHR_DEDICATED_ALLOCATION               = 1ull <<  5,
	EXTENSION_KHR_EXTERNAL_MEMORY                    = 1ull <<  6,
	EXTENSION_KHR_EXTERNAL_MEMORY_FD                 = 1ull <<  7,
	EXTENSION_KHR_EXTERNAL_SEMAPHORE                 = 1ull <<  8,
	EXTENSION_KHR_EXTERNAL_SEMAPHORE_FD              = 1ull <<  9,
	EXTENSION_KHR_GET_MEMORY_REQUIREMENTS_2          = 1ull << 10,
	EXTENSION_KHR_IMAGE_FORMAT_LIST                  = 1ull << 11,
	EXTENSION_KHR_INCREMENTAL_PRESENT                = 1ull << 12,
	EXTENSION_KHR_MAINTENANCE_1                      = 1ull << 13,
	EXTENSION_KHR_SAMPLER_YCBCR_CONVERSION           = 1ull << 14,
	EXTENSION_KHR_SWAPCHAIN                          = 1ull << 15,
};

enum vulkan_pipeline_texture_variant {
	PIPELINE_VARIANT_NONE = 0,
/* Keep the following in sync with Vulkan shader.frag. */
	PIPELINE_VARIANT_RGBA     = 1,
	PIPELINE_VARIANT_RGBX     = 2,
	PIPELINE_VARIANT_SOLID    = 3,
	PIPELINE_VARIANT_EXTERNAL = 4,
};

struct vulkan_pipeline_requirements
{
	unsigned texcoord_input:1; /* enum vulkan_shader_texcoord_input */
	unsigned variant:4; /* enum vulkan_pipeline_texture_variant */
	bool input_is_premult:1;
	bool blend:1;
	bool green_tint:1;
	VkRenderPass renderpass;
};

struct vulkan_pipeline_config {
	struct vulkan_pipeline_requirements req;

	struct weston_matrix projection;
	struct weston_matrix surface_to_buffer;
	float view_alpha;
	float unicolor[4];
};


/* Keep the following in sync with vertex.glsl. */
enum vulkan_shader_texcoord_input {
	SHADER_TEXCOORD_INPUT_ATTRIB = 0,
	SHADER_TEXCOORD_INPUT_SURFACE,
};

struct vulkan_pipeline {
	struct vulkan_pipeline_requirements key;

	struct wl_list link; /* vulkan_renderer::pipeline_list */
	struct timespec last_used;

	VkDescriptorSetLayout descriptor_set_layout;

	VkPipeline pipeline;
	VkPipelineLayout pipeline_layout;
};

struct vulkan_renderer_texture_image {
	VkImage image;
	VkDeviceMemory memory;
	VkImageView image_view;

	VkBuffer staging_buffer;
	VkDeviceMemory staging_memory;
	void *staging_map;

	VkCommandBuffer upload_cmd;
	VkFence upload_fence;
};

struct vulkan_renderer {
	struct weston_renderer base;
	struct weston_compositor *compositor;

	uint64_t inst_extensions;
	VkInstance inst;

	VkPhysicalDevice phys_dev;
	VkQueue queue;
	uint32_t queue_family;

	uint64_t device_extensions;
	bool semaphore_import_export;
	VkDevice dev;

	VkCommandPool cmd_pool;

	int drm_fd; /* drm device fd */
	struct weston_drm_format_array supported_formats;
	struct wl_list dmabuf_images;
	struct wl_list dmabuf_formats;

	struct wl_signal destroy_signal;
	struct wl_list pipeline_list;
	struct dmabuf_allocator *allocator;

#if defined(BUILD_WAYLAND_COMPOSITOR)
	PFN_vkCreateWaylandSurfaceKHR create_wayland_surface;
	PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR get_wayland_presentation_support;
#endif

#if defined(BUILD_X11_COMPOSITOR)
	PFN_vkCreateXcbSurfaceKHR create_xcb_surface;
	PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR get_xcb_presentation_support;
#endif

	PFN_vkGetImageMemoryRequirements2KHR get_image_memory_requirements2;
	PFN_vkGetMemoryFdPropertiesKHR get_memory_fd_properties;
	PFN_vkGetSemaphoreFdKHR get_semaphore_fd;
	PFN_vkImportSemaphoreFdKHR import_semaphore_fd;

	/* Debug modes. */
	struct weston_binding *debug_mode_binding;
	int debug_mode;

	/* This can be removed if a different shader is defined
	 * to avoid requiring a valid sampler descriptor to run
	 * for solids */
	struct {
		struct vulkan_renderer_texture_image image;
		VkSampler sampler;
	} dummy;
};

static inline struct vulkan_renderer *
get_renderer(struct weston_compositor *ec)
{
	return (struct vulkan_renderer *)ec->renderer;
}

static inline void pnext(void *base, void *next)
{
	VkBaseOutStructure *b = base;
	VkBaseOutStructure *n = next;
	n->pNext = b->pNext;
	b->pNext = n;
}

static inline void _check_vk_success(const char *file, int line, const char *func,
				     VkResult result, const char *vk_func)
{
	if (result == VK_SUCCESS)
		return;

	weston_log("%s %d %s Error: %s failed with VkResult %d\n", file, line, func, vk_func, result);
	abort();
}
#define check_vk_success(result, vk_func) \
	_check_vk_success(__FILE__, __LINE__, __func__, (result), (vk_func))

static inline bool
vulkan_instance_has(struct vulkan_renderer *vr,
		    enum vulkan_inst_ext_flag flag)
{
	return (vr->inst_extensions & ((uint64_t) flag)) == ((uint64_t) flag);
}

static inline bool
vulkan_device_has(struct vulkan_renderer *vr,
		  enum vulkan_device_ext_flag flag)
{
	return (vr->device_extensions & ((uint64_t) flag)) == ((uint64_t) flag);
}

void
vulkan_pipeline_destroy(struct vulkan_renderer *vr, struct vulkan_pipeline *pipeline);

void
vulkan_renderer_pipeline_list_destroy(struct vulkan_renderer *vr);

struct vulkan_pipeline *
vulkan_renderer_get_pipeline(struct vulkan_renderer *vr,
			     const struct vulkan_pipeline_requirements *reqs);

bool
vulkan_renderer_query_dmabuf_format(struct vulkan_renderer *vr,
				    const struct pixel_format_info *format);

bool
vulkan_renderer_query_shm_format(struct vulkan_renderer *vr,
				    const struct pixel_format_info *format);

#endif /* VULKAN_RENDERER_INTERNAL_H */
