/*
 * Copyright © 2016 Red Hat.
 * Copyright © 2016 Bas Nieuwenhuizen
 *
 * based in part on anv driver which is:
 * Copyright © 2015 Intel Corporation
 *
 * SPDX-License-Identifier: MIT
 */

#ifndef RADV_PHYSICAL_DEVICE_H
#define RADV_PHYSICAL_DEVICE_H

#include "ac_gpu_info.h"
#include "ac_perfcounter.h"

#include "radv_instance.h"
#include "radv_queue.h"
#include "radv_radeon_winsys.h"
#include "ac_vcn_enc.h"
#include "wsi_common.h"

#include "nir.h"

#include "vk_physical_device.h"

#ifndef _WIN32
#include <amdgpu.h>
#include <xf86drm.h>
#endif

/* The "RAW" clocks on Linux are called "FAST" on FreeBSD */
#if !defined(CLOCK_MONOTONIC_RAW) && defined(CLOCK_MONOTONIC_FAST)
#define CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC_FAST
#endif

struct radv_binning_settings {
   unsigned context_states_per_bin;    /* allowed range: [1, 6] */
   unsigned persistent_states_per_bin; /* allowed range: [1, 32] */
   unsigned fpovs_per_batch;           /* allowed range: [0, 255], 0 = unlimited */
};

struct radv_physical_device_cache_key {
   enum radeon_family family;
   uint32_t ptr_size;

   uint32_t conformant_trunc_coord : 1;
   uint32_t clear_lds : 1;
   uint32_t cs_wave32 : 1;
   uint32_t disable_aniso_single_level : 1;
   uint32_t disable_shrink_image_store : 1;
   uint32_t disable_sinking_load_input_fs : 1;
   uint32_t dual_color_blend_by_location : 1;
   uint32_t emulate_rt : 1;
   uint32_t ge_wave32 : 1;
   uint32_t invariant_geom : 1;
   uint32_t lower_discard_to_demote : 1;
   uint32_t no_fmask : 1;
   uint32_t no_ngg_gs : 1;
   uint32_t no_rt : 1;
   uint32_t ps_wave32 : 1;
   uint32_t rt_wave64 : 1;
   uint32_t split_fma : 1;
   uint32_t ssbo_non_uniform : 1;
   uint32_t tex_non_uniform : 1;
   uint32_t use_llvm : 1;
   uint32_t use_ngg : 1;
   uint32_t use_ngg_culling : 1;
};

enum radv_video_enc_hw_ver {
   RADV_VIDEO_ENC_HW_1_2,
   RADV_VIDEO_ENC_HW_2,
   RADV_VIDEO_ENC_HW_3,
   RADV_VIDEO_ENC_HW_4,
};

struct radv_physical_device {
   struct vk_physical_device vk;

   struct radeon_winsys *ws;
   struct radeon_info info;
   char name[VK_MAX_PHYSICAL_DEVICE_NAME_SIZE];
   char marketing_name[VK_MAX_PHYSICAL_DEVICE_NAME_SIZE];
   uint8_t driver_uuid[VK_UUID_SIZE];
   uint8_t device_uuid[VK_UUID_SIZE];
   uint8_t cache_uuid[VK_UUID_SIZE];

   int local_fd;
   int master_fd;
   struct wsi_device wsi_device;

   /* Whether DCC should be enabled for MSAA textures. */
   bool dcc_msaa_allowed;

   /* Whether to enable FMASK compression for MSAA textures (GFX6-GFX10.3) */
   bool use_fmask;

   /* Whether to enable NGG. */
   bool use_ngg;

   /* Whether to enable NGG culling. */
   bool use_ngg_culling;

   /* Whether to enable NGG streamout. */
   bool use_ngg_streamout;

   /* Whether to emulate the number of primitives generated by GS. */
   bool emulate_ngg_gs_query_pipeline_stat;

   /* Whether to use GS_FAST_LAUNCH(2) for mesh shaders. */
   bool mesh_fast_launch_2;

   /* Whether to emulate mesh/task shader queries. */
   bool emulate_mesh_shader_queries;

   /* Number of threads per wave. */
   uint8_t ps_wave_size;
   uint8_t cs_wave_size;
   uint8_t ge_wave_size;
   uint8_t rt_wave_size;

   /* Maximum compute shared memory size. */
   uint32_t max_shared_size;

   /* Whether to use the LLVM compiler backend */
   bool use_llvm;

   /* Whether to emulate ETC2 image support on HW without support. */
   bool emulate_etc2;

   /* Whether to emulate ASTC image support on HW without support. */
   bool emulate_astc;

   VkPhysicalDeviceMemoryProperties memory_properties;
   enum radeon_bo_domain memory_domains[VK_MAX_MEMORY_TYPES];
   enum radeon_bo_flag memory_flags[VK_MAX_MEMORY_TYPES];
   unsigned heaps;

   /* Bitmask of memory types that use the 32-bit address space. */
   uint32_t memory_types_32bit;

#ifndef _WIN32
   int available_nodes;
   drmPciBusInfo bus_info;

   dev_t primary_devid;
   dev_t render_devid;
#endif

   nir_shader_compiler_options nir_options[MESA_VULKAN_SHADER_STAGES];

   enum radv_queue_family vk_queue_to_radv[RADV_MAX_QUEUE_FAMILIES];
   uint32_t num_queues;

   uint32_t gs_table_depth;

   struct ac_hs_info hs;
   struct ac_task_info task_info;

   struct radv_binning_settings binning_settings;

   /* Performance counters. */
   struct ac_perfcounters ac_perfcounters;

   uint32_t num_perfcounters;
   struct radv_perfcounter_desc *perfcounters;

   struct {
      unsigned data0;
      unsigned data1;
      unsigned cmd;
      unsigned cntl;
   } vid_dec_reg;
   enum amd_ip_type vid_decode_ip;
   uint32_t vid_addr_gfx_mode;
   uint32_t stream_handle_base;
   uint32_t stream_handle_counter;
   uint32_t av1_version;
   rvcn_enc_cmd_t vcn_enc_cmds;
   enum radv_video_enc_hw_ver enc_hw_ver;
   uint32_t encoder_interface_version;
   bool video_encode_enabled;
   bool video_decode_enabled;
   struct radv_physical_device_cache_key cache_key;

   uint32_t tess_distribution_mode;
};

VK_DEFINE_HANDLE_CASTS(radv_physical_device, vk.base, VkPhysicalDevice, VK_OBJECT_TYPE_PHYSICAL_DEVICE)

static inline struct radv_instance *
radv_physical_device_instance(const struct radv_physical_device *pdev)
{
   return (struct radv_instance *)pdev->vk.instance;
}

static inline bool
radv_sparse_queue_enabled(const struct radv_physical_device *pdev)
{
   const struct radv_instance *instance = radv_physical_device_instance(pdev);

   /* Dedicated sparse queue requires VK_QUEUE_SUBMIT_MODE_THREADED, which is incompatible with
    * VK_DEVICE_TIMELINE_MODE_EMULATED. */
   return pdev->info.has_timeline_syncobj && !instance->drirc.legacy_sparse_binding;
}

static inline bool
radv_has_shader_buffer_float_minmax(const struct radv_physical_device *pdev, unsigned bitsize)
{
   return (pdev->info.gfx_level <= GFX7 && !pdev->use_llvm) || pdev->info.gfx_level == GFX10 ||
          pdev->info.gfx_level == GFX10_3 ||
          ((pdev->info.gfx_level == GFX11 || pdev->info.gfx_level == GFX11_5) && bitsize == 32);
}

static inline bool
radv_has_pops(const struct radv_physical_device *pdev)
{
   return pdev->info.gfx_level >= GFX9 && !pdev->use_llvm;
}

static inline bool
radv_has_uvd(struct radv_physical_device *pdev)
{
   enum radeon_family family = pdev->info.family;
   /* Only support UVD on TONGA+ */
   if (family < CHIP_TONGA)
      return false;
   return pdev->info.ip[AMD_IP_UVD].num_queues > 0;
}

static inline enum radv_queue_family
vk_queue_to_radv(const struct radv_physical_device *pdev, int queue_family_index)
{
   if (queue_family_index == VK_QUEUE_FAMILY_EXTERNAL || queue_family_index == VK_QUEUE_FAMILY_FOREIGN_EXT)
      return RADV_QUEUE_FOREIGN;
   if (queue_family_index == VK_QUEUE_FAMILY_IGNORED)
      return RADV_QUEUE_IGNORED;

   assert(queue_family_index < RADV_MAX_QUEUE_FAMILIES);
   return pdev->vk_queue_to_radv[queue_family_index];
}

/**
 * Helper used for debugging compiler issues by enabling/disabling LLVM for a
 * specific shader stage (developers only).
 */
static inline bool
radv_use_llvm_for_stage(const struct radv_physical_device *pdev, UNUSED gl_shader_stage stage)
{
   return pdev->use_llvm;
}

bool radv_enable_rt(const struct radv_physical_device *pdev, bool rt_pipelines);

bool radv_emulate_rt(const struct radv_physical_device *pdev);

uint32_t radv_find_memory_index(const struct radv_physical_device *pdev, VkMemoryPropertyFlags flags);

VkResult create_null_physical_device(struct vk_instance *vk_instance);

VkResult create_drm_physical_device(struct vk_instance *vk_instance, struct _drmDevice *device,
                                    struct vk_physical_device **out);

void radv_physical_device_destroy(struct vk_physical_device *vk_pdev);

#endif /* RADV_PHYSICAL_DEVICE_H */
