/*
 * Copyright © 2022 Imagination Technologies Ltd.
 *
 * based in part on anv driver which is:
 * Copyright © 2015 Intel Corporation
 *
 * based in part on v3dv driver which is:
 * Copyright © 2019 Raspberry Pi
 *
 * SPDX-License-Identifier: MIT
 */

#include "pvr_physical_device.h"

#include <sys/sysmacros.h>

#include "git_sha1.h"

#include "util/disk_cache.h"
#include "util/ralloc.h"

#include "vk_util.h"
#include "vk_log.h"

#include "hwdef/rogue_hw_utils.h"

#include "pco/pco.h"
#include "pco_uscgen_programs.h"

#include "pvr_device.h"
#include "pvr_dump_info.h"
#include "pvr_entrypoints.h"
#include "pvr_instance.h"
#include "pvr_macros.h"
#include "pvr_winsys.h"
#include "pvr_wsi.h"

#define VK_VENDOR_ID_IMAGINATION 0x1010

void pvr_physical_device_dump_info(const struct pvr_physical_device *pdevice,
                                   char *const *comp_display,
                                   char *const *comp_render)
{
   drmVersionPtr version_display = NULL, version_render;
   struct pvr_device_dump_info info = { 0 };

   if (pdevice->ws->display_fd >= 0)
      version_display = drmGetVersion(pdevice->ws->display_fd);

   version_render = drmGetVersion(pdevice->ws->render_fd);
   if (!version_render) {
      drmFreeVersion(version_display);
      return;
   }

   info.device_info = &pdevice->dev_info;
   info.device_runtime_info = &pdevice->dev_runtime_info;
   if (version_display) {
      info.drm_display.patchlevel = version_display->version_patchlevel;
      info.drm_display.major = version_display->version_major;
      info.drm_display.minor = version_display->version_minor;
      info.drm_display.name = version_display->name;
      info.drm_display.date = version_display->date;
      info.drm_display.comp = comp_display;
   }
   info.drm_render.patchlevel = version_render->version_patchlevel;
   info.drm_render.major = version_render->version_major;
   info.drm_render.minor = version_render->version_minor;
   info.drm_render.name = version_render->name;
   info.drm_render.date = version_render->date;
   info.drm_render.comp = comp_render;

   pvr_dump_physical_device_info(&info);

   drmFreeVersion(version_display);
   drmFreeVersion(version_render);
}

void pvr_physical_device_destroy(struct vk_physical_device *vk_pdevice)
{
   struct pvr_physical_device *pdevice =
      container_of(vk_pdevice, struct pvr_physical_device, vk);

   /* Be careful here. The device might not have been initialized. This can
    * happen since initialization is done in vkEnumeratePhysicalDevices() but
    * finish is done in vkDestroyInstance(). Make sure that you check for NULL
    * before freeing or that the freeing functions accept NULL pointers.
    */

   ralloc_free(pdevice->pco_ctx);

   pvr_wsi_finish(pdevice);

   pvr_physical_device_free_pipeline_cache(pdevice);

   if (pdevice->ws)
      pvr_winsys_destroy(pdevice->ws);

   vk_free(&pdevice->vk.instance->alloc, pdevice->render_path);
   vk_free(&pdevice->vk.instance->alloc, pdevice->display_path);

   vk_physical_device_finish(&pdevice->vk);

   vk_free(&pdevice->vk.instance->alloc, pdevice);
}

void pvr_physical_device_free_pipeline_cache(
   struct pvr_physical_device *const pdevice)
{
#ifdef ENABLE_SHADER_CACHE
   if (!pdevice->vk.disk_cache)
      return;

   disk_cache_destroy(pdevice->vk.disk_cache);
   pdevice->vk.disk_cache = NULL;
#else
   assert(pdevice->vk.disk_cache);
#endif /* ENABLE_SHADER_CACHE */
}

static void pvr_physical_device_get_supported_extensions(
   struct vk_device_extension_table *extensions)
{
   *extensions = (struct vk_device_extension_table){
      .KHR_bind_memory2 = true,
      .KHR_buffer_device_address = false,
      .KHR_copy_commands2 = true,
      .KHR_create_renderpass2 = true,
      .KHR_dedicated_allocation = true,
      .KHR_depth_stencil_resolve = true,
      .KHR_descriptor_update_template = true,
      .KHR_device_group = true,
      .KHR_driver_properties = true,
      .KHR_dynamic_rendering = true,
      .KHR_external_fence = true,
      .KHR_external_fence_fd = true,
      .KHR_external_memory = true,
      .KHR_external_memory_fd = true,
      .KHR_external_semaphore = PVR_USE_WSI_PLATFORM,
      .KHR_external_semaphore_fd = PVR_USE_WSI_PLATFORM,
      .KHR_format_feature_flags2 = false,
      .KHR_get_memory_requirements2 = true,
      .KHR_incremental_present = PVR_USE_WSI_PLATFORM,
      .KHR_image_format_list = true,
      .KHR_imageless_framebuffer = true,
      .KHR_index_type_uint8 = false,
      .KHR_line_rasterization = true,
      .KHR_maintenance1 = true,
      .KHR_maintenance2 = true,
      .KHR_maintenance3 = true,
      .KHR_maintenance4 = true,
      .KHR_map_memory2 = true,
      .KHR_multiview = true,
      .KHR_pipeline_executable_properties = true,
      .KHR_present_id2 = PVR_USE_WSI_PLATFORM,
      .KHR_present_wait2 = PVR_USE_WSI_PLATFORM,
      .KHR_relaxed_block_layout = true,
      .KHR_robustness2 = true,
      .KHR_sampler_mirror_clamp_to_edge = true,
      .KHR_sampler_ycbcr_conversion = true,
      .KHR_separate_depth_stencil_layouts = true,
      .KHR_shader_draw_parameters = true,
      .KHR_shader_expect_assume = false,
      .KHR_shader_float_controls = true,
      .KHR_shader_integer_dot_product = true,
      .KHR_shader_non_semantic_info = true,
      .KHR_shader_relaxed_extended_instruction = true,
      .KHR_shader_subgroup_extended_types = true,
      .KHR_shader_terminate_invocation = true,
      .KHR_spirv_1_4 = true,
      .KHR_storage_buffer_storage_class = true,
      .KHR_swapchain = PVR_USE_WSI_PLATFORM,
      .KHR_swapchain_mutable_format = PVR_USE_WSI_PLATFORM,
      .KHR_timeline_semaphore = true,
      .KHR_uniform_buffer_standard_layout = true,
      .KHR_vertex_attribute_divisor = true,
      .KHR_zero_initialize_workgroup_memory = false,
      .EXT_border_color_swizzle = true,
      .EXT_color_write_enable = true,
      .EXT_custom_border_color = true,
      .EXT_depth_clamp_zero_one = true,
      .EXT_depth_clip_enable = true,
      .EXT_image_drm_format_modifier = true,
      .EXT_extended_dynamic_state = true,
      .EXT_extended_dynamic_state2 = true,
      .EXT_extended_dynamic_state3 = true,
      .EXT_external_memory_dma_buf = true,
      .EXT_host_query_reset = true,
      .EXT_image_2d_view_of_3d = true,
      .EXT_index_type_uint8 = false,
      .EXT_line_rasterization = true,
      .EXT_map_memory_placed = true,
      .EXT_non_seamless_cube_map = true,
      .EXT_physical_device_drm = true,
      .EXT_private_data = true,
      .EXT_provoking_vertex = true,
      .EXT_queue_family_foreign = true,
      .EXT_robustness2 = true,
      .EXT_scalar_block_layout = true,
      .EXT_separate_stencil_usage = true,
      .EXT_shader_demote_to_helper_invocation = true,
      .EXT_shader_replicated_composites = true,
      .EXT_texel_buffer_alignment = false,
      .EXT_tooling_info = true,
      .EXT_vertex_attribute_divisor = true,
      .EXT_zero_initialize_device_memory = true,
   };
}

static void pvr_physical_device_get_supported_features(
   const struct pvr_device_info *const dev_info,
   struct vk_features *const features)
{
   *features = (struct vk_features){
      /* Vulkan 1.0 */
      .robustBufferAccess = true,
      .fullDrawIndexUint32 = true,
      .imageCubeArray = true,
      .independentBlend = true,
      .geometryShader = false,
      .tessellationShader = false,
      .sampleRateShading = true,
      .dualSrcBlend = false,
      .logicOp = true,
      .multiDrawIndirect = true,
      .drawIndirectFirstInstance = true,
      .depthClamp = false,
      .depthBiasClamp = true,
      .fillModeNonSolid = false,
      .depthBounds = false,
      .wideLines = true,
      .largePoints = true,
      .alphaToOne = true,
      .multiViewport = false,
      .samplerAnisotropy = true,
      .textureCompressionETC2 = true,
      .textureCompressionASTC_LDR = false,
      .textureCompressionBC = false,
      .occlusionQueryPrecise = false,
      .pipelineStatisticsQuery = false,
      .vertexPipelineStoresAndAtomics = false,
      .fragmentStoresAndAtomics = false,
      .shaderTessellationAndGeometryPointSize = false,
      .shaderImageGatherExtended = false,
      .shaderStorageImageExtendedFormats = true,
      .shaderStorageImageMultisample = false,
      .shaderStorageImageReadWithoutFormat = true,
      .shaderStorageImageWriteWithoutFormat = true,
      .shaderUniformBufferArrayDynamicIndexing = false,
      .shaderSampledImageArrayDynamicIndexing = false,
      .shaderStorageBufferArrayDynamicIndexing = false,
      .shaderStorageImageArrayDynamicIndexing = false,
      .shaderClipDistance = true,
      .shaderCullDistance = true,
      .shaderFloat64 = false,
      .shaderInt64 = false,
      .shaderInt16 = false,
      .shaderResourceResidency = false,
      .shaderResourceMinLod = false,
      .sparseBinding = false,
      .sparseResidencyBuffer = false,
      .sparseResidencyImage2D = false,
      .sparseResidencyImage3D = false,
      .sparseResidency2Samples = false,
      .sparseResidency4Samples = false,
      .sparseResidency8Samples = false,
      .sparseResidency16Samples = false,
      .sparseResidencyAliased = false,
      .variableMultisampleRate = false,
      .inheritedQueries = false,

      /* Vulkan 1.1 */
      .storageBuffer16BitAccess = false,
      .uniformAndStorageBuffer16BitAccess = false,
      .storagePushConstant16 = false,
      .storageInputOutput16 = false,
      .variablePointers = false,
      .protectedMemory = false,
      .samplerYcbcrConversion = true,

      /* Vulkan 1.2 */
      .samplerMirrorClampToEdge = true,
      .drawIndirectCount = false,
      .storageBuffer8BitAccess = false,
      .uniformAndStorageBuffer8BitAccess = false,
      .storagePushConstant8 = false,
      .shaderBufferInt64Atomics = false,
      .shaderSharedInt64Atomics = false,
      .shaderFloat16 = false,
      .shaderInt8 = false,
      .descriptorIndexing = false,
      .shaderInputAttachmentArrayDynamicIndexing = false,
      .shaderUniformTexelBufferArrayDynamicIndexing = false,
      .shaderStorageTexelBufferArrayDynamicIndexing = false,
      .shaderUniformBufferArrayNonUniformIndexing = false,
      .shaderSampledImageArrayNonUniformIndexing = false,
      .shaderStorageBufferArrayNonUniformIndexing = false,
      .shaderStorageImageArrayNonUniformIndexing = false,
      .shaderInputAttachmentArrayNonUniformIndexing = false,
      .shaderUniformTexelBufferArrayNonUniformIndexing = false,
      .shaderStorageTexelBufferArrayNonUniformIndexing = false,
      .descriptorBindingUniformBufferUpdateAfterBind = false,
      .descriptorBindingSampledImageUpdateAfterBind = false,
      .descriptorBindingStorageImageUpdateAfterBind = false,
      .descriptorBindingStorageBufferUpdateAfterBind = false,
      .descriptorBindingUniformTexelBufferUpdateAfterBind = false,
      .descriptorBindingStorageTexelBufferUpdateAfterBind = false,
      .descriptorBindingUpdateUnusedWhilePending = false,
      .descriptorBindingPartiallyBound = false,
      .descriptorBindingVariableDescriptorCount = false,
      .runtimeDescriptorArray = false,
      .samplerFilterMinmax = false,
      .vulkanMemoryModel = false,
      .vulkanMemoryModelDeviceScope = false,
      .vulkanMemoryModelAvailabilityVisibilityChains = false,
      .shaderOutputViewportIndex = false,
      .shaderOutputLayer = false,
      .subgroupBroadcastDynamicId = true,

      /* VK_EXT_depth_clamp_zero_one */
      .depthClampZeroOne = true,

      /* VK_KHR_index_type_uint8 */
      .indexTypeUint8 = true,

      /* Vulkan 1.2 / VK_KHR_imageless_framebuffer */
      .imagelessFramebuffer = true,

      /* Vulkan 1.1 / VK_KHR_multiview */
      .multiview = true,
      .multiviewGeometryShader = false,
      .multiviewTessellationShader = false,

      /* Vulkan 1.3 / VK_KHR_maintenance4 */
      .maintenance4 = true,

      /* Vulkan 1.1 / VK_KHR_shader_draw_parameters */
      .shaderDrawParameters = true,

      /* Vulkan 1.3 / VK_KHR_shader_integer_dot_product */
      .shaderIntegerDotProduct = true,

      /* Vulkan 1.2 / VK_KHR_timeline_semaphore */
      .timelineSemaphore = true,

      /* Vulkan 1.2 / VK_KHR_separate_depth_stencil_layouts */
      .separateDepthStencilLayouts = true,

      /* VK_KHR_shader_relaxed_extended_instruction */
      .shaderRelaxedExtendedInstruction = true,

      /* Vulkan 1.2 / VK_KHR_shader_subgroup_extended_types */
      .shaderSubgroupExtendedTypes = true,

      /* Vulkan 1.1 / VK_KHR_robustness2 */
      .robustBufferAccess2 = false,
      .robustImageAccess2 = false,
      .nullDescriptor = true,

      /* Vulkan 1.2 / VK_KHR_uniform_buffer_standard_layout */
      .uniformBufferStandardLayout = true,

      /* VK_EXT_color_write_enable */
      .colorWriteEnable = true,

      /* Vulkan 1.3 / VK_EXT_extended_dynamic_state */
      .extendedDynamicState = true,

      /* Vulkan 1.3 / VK_EXT_extended_dynamic_state2 */
      .extendedDynamicState2 = true,
      .extendedDynamicState2LogicOp = false,
      .extendedDynamicState2PatchControlPoints = false,

      /* VK_EXT_extended_dynamic_state3 */
      .extendedDynamicState3TessellationDomainOrigin = false,
      .extendedDynamicState3DepthClampEnable = false,
      .extendedDynamicState3PolygonMode = false,
      .extendedDynamicState3RasterizationSamples = true,
      .extendedDynamicState3SampleMask = true,
      .extendedDynamicState3AlphaToCoverageEnable = true,
      .extendedDynamicState3AlphaToOneEnable = true,
      .extendedDynamicState3LogicOpEnable = false,
      .extendedDynamicState3ColorBlendEnable = false,
      .extendedDynamicState3ColorBlendEquation = false,
      .extendedDynamicState3ColorWriteMask = false,
      .extendedDynamicState3RasterizationStream = false,
      .extendedDynamicState3ConservativeRasterizationMode = false,
      .extendedDynamicState3ExtraPrimitiveOverestimationSize = false,
      .extendedDynamicState3DepthClipEnable = false,
      .extendedDynamicState3SampleLocationsEnable = false,
      .extendedDynamicState3ColorBlendAdvanced = false,
      .extendedDynamicState3ProvokingVertexMode = false,
      .extendedDynamicState3LineRasterizationMode = false,
      .extendedDynamicState3LineStippleEnable = false,
      .extendedDynamicState3DepthClipNegativeOneToOne = false,
      .extendedDynamicState3ViewportWScalingEnable = false,
      .extendedDynamicState3ViewportSwizzle = false,
      .extendedDynamicState3CoverageToColorEnable = false,
      .extendedDynamicState3CoverageToColorLocation = false,
      .extendedDynamicState3CoverageModulationMode = false,
      .extendedDynamicState3CoverageModulationTableEnable = false,
      .extendedDynamicState3CoverageModulationTable = false,
      .extendedDynamicState3CoverageReductionMode = false,
      .extendedDynamicState3RepresentativeFragmentTestEnable = false,
      .extendedDynamicState3ShadingRateImageEnable = false,

      /* Vulkan 1.2 / VK_EXT_host_query_reset */
      .hostQueryReset = true,

      /* VK_EXT_image_2d_view_of_3d */
      .image2DViewOf3D = true,
      .sampler2DViewOf3D = true,

      /* VK_EXT_map_memory_placed */
      .memoryMapPlaced = true,
      .memoryMapRangePlaced = false,
      .memoryUnmapReserve = true,

      /* VK_EXT_non_seamless_cube_map */
      .nonSeamlessCubeMap = true,

      /* Vulkan 1.3 / VK_EXT_private_data */
      .privateData = true,

      /* VK_EXT_provoking_vertex */
      .provokingVertexLast = true,
      .transformFeedbackPreservesProvokingVertex = false,

      /* Vulkan 1.2 / VK_EXT_scalar_block_layout */
      .scalarBlockLayout = true,

      /* Vulkan 1.3 / VK_EXT_texel_buffer_alignment */
      .texelBufferAlignment = true,

      /* Vulkan 1.2 / VK_KHR_buffer_device_address */
      .bufferDeviceAddress = false,
      .bufferDeviceAddressCaptureReplay = false,
      .bufferDeviceAddressMultiDevice = false,

      /* VK_KHR_shader_expect_assume */
      .shaderExpectAssume = false,

      /* VK_EXT_shader_demote_to_helper_invocation */
      .shaderDemoteToHelperInvocation = true,

      /* VK_EXT_shader_replicated_composites */
      .shaderReplicatedComposites = true,

      /* VK_KHR_shader_terminate_invocation */
      .shaderTerminateInvocation = true,

      /* VK_KHR_present_id2 */
      .presentId2 = PVR_USE_WSI_PLATFORM,

      /* VK_KHR_present_wait2 */
      .presentWait2 = PVR_USE_WSI_PLATFORM,

      /* Vulkan 1.4 / VK_EXT_vertex_attribute_divisor /
         VK_KHR_vertex_attribute_divisor */
      .vertexAttributeInstanceRateDivisor = true,
      .vertexAttributeInstanceRateZeroDivisor = true,

      /* Vulkan 1.3 / VK_KHR_zero_initialize_workgroup_memory */
      .shaderZeroInitializeWorkgroupMemory = false,

      /* VK_EXT_border_color_swizzle */
      .borderColorSwizzle = true,
      .borderColorSwizzleFromImage = true,

      /* VK_EXT_custom_border_color */
      .customBorderColors = true,

      /* VK_EXT_depth_clip_enable */
      .depthClipEnable = true,

      /* VK_KHR_line_rasterization */
      .bresenhamLines = true,

      /* VK_EXT_zero_initialize_device_memory */
      .zeroInitializeDeviceMemory = true,

      /* Vulkan 1.2 / VK_KHR_dynamic_rendering */
      .dynamicRendering = true,

      /* VK_KHR_pipeline_executable_properties */
      .pipelineExecutableInfo = true,
   };
}

static uint32_t get_api_version(void)
{
   const uint32_t version_override = vk_get_version_override();
   if (version_override)
      return version_override;

   return VK_MAKE_API_VERSION(0, 1, 2, VK_HEADER_VERSION);
}

static unsigned
get_custom_border_color_samplers(const struct pvr_device_info *dev_info);

static bool pvr_physical_device_get_properties(
   const struct pvr_physical_device *const pdevice,
   struct vk_properties *const properties)
{
   const struct pvr_device_info *const dev_info = &pdevice->dev_info;
   const struct pvr_device_runtime_info *dev_runtime_info =
      &pdevice->dev_runtime_info;

   /* Default value based on the minimum value found in all existing cores. */
   const uint32_t max_multisample =
      PVR_GET_FEATURE_VALUE(dev_info, max_multisample, 4);

   UNUSED const uint32_t sub_pixel_precision =
      PVR_HAS_FEATURE(dev_info, simple_internal_parameter_format) ? 4U : 8U;

   UNUSED const uint32_t max_render_size = rogue_get_render_size_max(dev_info);

   UNUSED const uint32_t max_sample_bits = ((max_multisample << 1) - 1);

   UNUSED const uint32_t max_user_vertex_components =
      pvr_get_max_user_vertex_output_components(dev_info);

   const bool usc_alu_roundingmode_rne =
      PVR_HAS_FEATURE(dev_info, usc_alu_roundingmode_rne);

   /* The workgroup invocations are limited by the case where we have a compute
    * barrier - each slot has a fixed number of invocations, the whole workgroup
    * may need to span multiple slots. As each slot will WAIT at the barrier
    * until the last invocation completes, all have to be schedulable at the
    * same time.
    *
    * Typically all Rogue cores have 16 slots. Some of the smallest cores are
    * reduced to 14.
    *
    * The compute barrier slot exhaustion scenario can be tested with:
    * dEQP-VK.memory_model.message_passing*u32.coherent.fence_fence
    *    .atomicwrite*guard*comp
    */

   /* Default value based on the minimum value found in all existing cores. */
   const uint32_t usc_slots = PVR_GET_FEATURE_VALUE(dev_info, usc_slots, 14);

   /* Default value based on the minimum value found in all existing cores. */
   const uint32_t max_instances_per_pds_task =
      PVR_GET_FEATURE_VALUE(dev_info, max_instances_per_pds_task, 32U);

   UNUSED const uint32_t max_compute_work_group_invocations =
      (usc_slots * max_instances_per_pds_task >= 512U) ? 512U : 384U;

   assert(pdevice->memory.memoryHeapCount == 1);
   const VkDeviceSize max_memory_alloc_size =
      pdevice->memory.memoryHeaps[0].size;

   const uint32_t line_sub_pixel_precision_bits =
      PVR_HAS_FEATURE(dev_info, simple_internal_parameter_format) ? 4U : 8U;

   *properties = (struct vk_properties){
      /* Vulkan 1.0 */
      .apiVersion = get_api_version(),
      .driverVersion = vk_get_driver_version(),
      .vendorID = VK_VENDOR_ID_IMAGINATION,
      .deviceID = dev_info->ident.device_id,
      .deviceType = VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU,
      /* deviceName and pipelineCacheUUID are filled below .*/

      .maxImageDimension1D = 4096U,
      .maxImageDimension2D = 4096U,
      .maxImageDimension3D = 256U,
      .maxImageDimensionCube = 4096U,
      .maxImageArrayLayers = 256U,
      .maxTexelBufferElements = 64U * 1024U,
      .maxUniformBufferRange = 16U * 1024U,
      .maxStorageBufferRange = 128U * 1024U * 1024U,
      .maxPushConstantsSize = 128U,
      .maxMemoryAllocationCount = 4096U,
      .maxSamplerAllocationCount = 4000U,
      .bufferImageGranularity = 1U,
      .sparseAddressSpaceSize = 0U, /* Requires sparseBinding */
      .maxBoundDescriptorSets = 4U,
      .maxPerStageDescriptorSamplers = 16,
      .maxPerStageDescriptorUniformBuffers = 12,
      .maxPerStageDescriptorStorageBuffers = 8,
      .maxPerStageDescriptorSampledImages = 16,
      .maxPerStageDescriptorStorageImages = 4,
      .maxPerStageDescriptorInputAttachments = 4,
      .maxPerStageResources = 48,
      .maxDescriptorSetSamplers = 3U * 16U,
      .maxDescriptorSetUniformBuffers = 3U * 12U,
      .maxDescriptorSetUniformBuffersDynamic = 8U,
      .maxDescriptorSetStorageBuffers = 3U * 4U,
      .maxDescriptorSetStorageBuffersDynamic = 4U,
      .maxDescriptorSetSampledImages = 3U * 16U,
      .maxDescriptorSetStorageImages = 3U * 4U,
      .maxDescriptorSetInputAttachments = 4U,

      /* Vertex Shader Limits */
      .maxVertexInputAttributes = 16U,
      .maxVertexInputBindings = 16U,
      .maxVertexInputAttributeOffset = 2048U - 1U,
      .maxVertexInputBindingStride = 2048U,
      .maxVertexOutputComponents = 64U,

      /* Tessellation Limits */
      /* Requires tessellationShader */
      .maxTessellationGenerationLevel = 0U,
      .maxTessellationPatchSize = 0U,
      .maxTessellationControlPerVertexInputComponents = 0U,
      .maxTessellationControlPerVertexOutputComponents = 0U,
      .maxTessellationControlPerPatchOutputComponents = 0U,
      .maxTessellationControlTotalOutputComponents = 0U,
      .maxTessellationEvaluationInputComponents = 0U,
      .maxTessellationEvaluationOutputComponents = 0U,

      /* Geometry Shader Limits */
      /* Requires geometryShader */
      .maxGeometryShaderInvocations = 0U,
      .maxGeometryInputComponents = 0U,
      .maxGeometryOutputComponents = 0U,
      .maxGeometryOutputVertices = 0U,
      .maxGeometryTotalOutputComponents = 0U,

      /* Fragment Shader Limits */
      .maxFragmentInputComponents = 64U,
      .maxFragmentOutputAttachments = 4U,
      .maxFragmentDualSrcAttachments = 0U, /* Requires dualSrcBlend */
      .maxFragmentCombinedOutputResources = 4U,

      /* Compute Shader Limits */
      .maxComputeSharedMemorySize = 16U * 1024U,
      .maxComputeWorkGroupCount = {
         [0] = (64U * 1024U) - 1,
         [1] = (64U * 1024U) - 1,
         [2] = (64U * 1024U) - 1,
      },
      .maxComputeWorkGroupInvocations = 128U,
      .maxComputeWorkGroupSize = {
        [0] = 128U,
        [1] = 128U,
        [2] = 64U,
      },

      /* Rasterization Limits */
      .subPixelPrecisionBits = 4U,
      .subTexelPrecisionBits = 8U,
      .mipmapPrecisionBits = 8U,

      .maxDrawIndexedIndexValue = UINT32_MAX,
      .maxDrawIndirectCount = 2U * 1024U * 1024U * 1024U,
      .maxSamplerLodBias = 16.0f,
      .maxSamplerAnisotropy = 16.0f, /* Requires samplerAnisotropy */
      .maxViewports = 1U, /* Requires multiViewport */

      .maxViewportDimensions[0] = 4096U,
      .maxViewportDimensions[1] = 4096U,
      .viewportBoundsRange[0] = -8192.0f,
      .viewportBoundsRange[1] = 8191.0f,

      .viewportSubPixelBits = 0U,
      .minMemoryMapAlignment = pdevice->ws->page_size,
      .minTexelBufferOffsetAlignment = PVR_TEXEL_BUFFER_OFFSET_ALIGNMENT,
      .minUniformBufferOffsetAlignment = PVR_UNIFORM_BUFFER_OFFSET_ALIGNMENT,
      .minStorageBufferOffsetAlignment = PVR_STORAGE_BUFFER_OFFSET_ALIGNMENT,

      .minTexelOffset = -8,
      .maxTexelOffset = 7U,

      /* Requires shaderImageGatherExtended */
      .minTexelGatherOffset = 0,
      .maxTexelGatherOffset = 0U,

      /* Requires sampleRateShading */
      .minInterpolationOffset = -0.5f,
      .maxInterpolationOffset = 0.5f,
      .subPixelInterpolationOffsetBits = 4U,

      .maxFramebufferWidth = 4096U,
      .maxFramebufferHeight = 4096U,
      .maxFramebufferLayers = 256U,

      /* Note: update nir_shader_compiler_options.max_samples when changing this. */
      .framebufferColorSampleCounts = VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT,
      .framebufferDepthSampleCounts = VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT,
      .framebufferStencilSampleCounts = VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT,
      .framebufferNoAttachmentsSampleCounts = VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT,
      .maxColorAttachments = 4U,
      .sampledImageColorSampleCounts = VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT,
      .sampledImageIntegerSampleCounts = VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT,
      .sampledImageDepthSampleCounts = VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT,
      .sampledImageStencilSampleCounts = VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT,
      .storageImageSampleCounts = VK_SAMPLE_COUNT_1_BIT, /* Requires shaderStorageImageMultisample */
      .maxSampleMaskWords = 1U,
      .timestampComputeAndGraphics = false,
      .timestampPeriod = 0.0f,

      .maxClipDistances = PVR_MAX_USER_PLANES,
      .maxCullDistances = PVR_MAX_USER_PLANES,
      .maxCombinedClipAndCullDistances = PVR_MAX_USER_PLANES,

      .discreteQueuePriorities = 2U,

      .pointSizeRange[0] = PVR_POINT_SIZE_RANGE_MIN,
      .pointSizeRange[1] = PVR_POINT_SIZE_RANGE_MAX,
      .pointSizeGranularity = PVR_POINT_SIZE_GRANULARITY,

      .lineWidthRange[0] = PVR_LINE_WIDTH_MIN,
      .lineWidthRange[1] = PVR_LINE_WIDTH_MAX,
      .lineWidthGranularity = PVR_LINE_WIDTH_GRANULARITY,

      .strictLines = false,
      .standardSampleLocations = true,
      .optimalBufferCopyOffsetAlignment = PVR_STORAGE_BUFFER_OFFSET_ALIGNMENT,
      .optimalBufferCopyRowPitchAlignment = PVR_STORAGE_BUFFER_OFFSET_ALIGNMENT,
      .nonCoherentAtomSize = 1U,

      /* Vulkan 1.1 */
      .subgroupSize = 1,
      .subgroupSupportedStages = VK_SHADER_STAGE_COMPUTE_BIT,
      .subgroupSupportedOperations = VK_SUBGROUP_FEATURE_BASIC_BIT,
      .subgroupQuadOperationsInAllStages = false,
      .protectedNoFault = false,

      /* Vulkan 1.2 */
      .maxUpdateAfterBindDescriptorsInAllPools = 0,
      .shaderUniformBufferArrayNonUniformIndexingNative = false,
      .shaderSampledImageArrayNonUniformIndexingNative = false,
      .shaderStorageBufferArrayNonUniformIndexingNative = false,
      .shaderStorageImageArrayNonUniformIndexingNative = false,
      .shaderInputAttachmentArrayNonUniformIndexingNative = false,
      .robustBufferAccessUpdateAfterBind = false,
      .quadDivergentImplicitLod = false,
      .maxPerStageDescriptorUpdateAfterBindSamplers = 0,
      .maxPerStageDescriptorUpdateAfterBindUniformBuffers = 0,
      .maxPerStageDescriptorUpdateAfterBindStorageBuffers = 0,
      .maxPerStageDescriptorUpdateAfterBindSampledImages = 0,
      .maxPerStageDescriptorUpdateAfterBindStorageImages = 0,
      .maxPerStageDescriptorUpdateAfterBindInputAttachments = 0,
      .maxPerStageUpdateAfterBindResources = 0,
      .maxDescriptorSetUpdateAfterBindSamplers = 0,
      .maxDescriptorSetUpdateAfterBindUniformBuffers = 0,
      .maxDescriptorSetUpdateAfterBindUniformBuffersDynamic = 0,
      .maxDescriptorSetUpdateAfterBindStorageBuffers = 0,
      .maxDescriptorSetUpdateAfterBindStorageBuffersDynamic = 0,
      .maxDescriptorSetUpdateAfterBindSampledImages = 0,
      .maxDescriptorSetUpdateAfterBindStorageImages = 0,
      .maxDescriptorSetUpdateAfterBindInputAttachments = 0,
      .filterMinmaxSingleComponentFormats = false,
      .filterMinmaxImageComponentMapping = false,
      .framebufferIntegerColorSampleCounts =
         VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT,

      /* Vulkan 1.0 / VK_KHR_maintenance2 */
      .pointClippingBehavior =
         VK_POINT_CLIPPING_BEHAVIOR_USER_CLIP_PLANES_ONLY,

      /* Vulkan 1.1 / VK_KHR_maintenance3 */
      .maxPerSetDescriptors = PVR_MAX_DESCRIPTORS_PER_SET,
      .maxMemoryAllocationSize = max_memory_alloc_size,

      /* Vulkan 1.1 / VK_KHR_multiview */
      .maxMultiviewViewCount = PVR_MAX_MULTIVIEW,
      .maxMultiviewInstanceIndex = (1 << 27) - 1,

      /* Vulkan 1.2 / VK_KHR_driver_properties */
      .driverID = VK_DRIVER_ID_IMAGINATION_OPEN_SOURCE_MESA,
      .driverName = "Imagination open-source Mesa driver",
      .driverInfo = "Mesa " PACKAGE_VERSION MESA_GIT_SHA1,
      .conformanceVersion = {
         .major = 1,
         .minor = 3,
         .subminor = 8,
         .patch = 4,
      },

      /* VK_EXT_extended_dynamic_state3 */
      .dynamicPrimitiveTopologyUnrestricted = false,

      /* VK_EXT_map_memory_placed */
      .minPlacedMemoryMapAlignment = pdevice->ws->page_size,

      /* VK_EXT_provoking_vertex */
      .provokingVertexModePerPipeline = true,
      .transformFeedbackPreservesTriangleFanProvokingVertex = false,

      /* Vulkan 1.1 / VK_KHR_robustness2 */
      .robustStorageBufferAccessSizeAlignment = PVR_STORAGE_BUFFER_OFFSET_ALIGNMENT,
      .robustUniformBufferAccessSizeAlignment = PVR_STORAGE_BUFFER_OFFSET_ALIGNMENT,

      /* Vulkan 1.2 / VK_KHR_shader_float_controls */
      .denormBehaviorIndependence =
         VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY,
      .roundingModeIndependence = VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_NONE,
      .shaderSignedZeroInfNanPreserveFloat16 = true,
      .shaderSignedZeroInfNanPreserveFloat32 = true,
      .shaderSignedZeroInfNanPreserveFloat64 = true,
      .shaderDenormPreserveFloat16 = true,
      .shaderDenormPreserveFloat32 = false,
      .shaderDenormPreserveFloat64 = true,
      .shaderDenormFlushToZeroFloat16 = false,
      .shaderDenormFlushToZeroFloat32 = false,
      .shaderDenormFlushToZeroFloat64 = false,
      .shaderRoundingModeRTEFloat16 = usc_alu_roundingmode_rne,
      .shaderRoundingModeRTEFloat32 = usc_alu_roundingmode_rne,
      .shaderRoundingModeRTEFloat64 = usc_alu_roundingmode_rne,
      .shaderRoundingModeRTZFloat16 = !usc_alu_roundingmode_rne,
      .shaderRoundingModeRTZFloat32 = !usc_alu_roundingmode_rne,
      .shaderRoundingModeRTZFloat64 = !usc_alu_roundingmode_rne,

      /* Vulkan 1.3 / VK_KHR_shader_integer_dot_product */
      .integerDotProduct8BitUnsignedAccelerated = false,
      .integerDotProduct8BitSignedAccelerated = false,
      .integerDotProduct8BitMixedSignednessAccelerated = false,
      .integerDotProduct4x8BitPackedUnsignedAccelerated = false,
      .integerDotProduct4x8BitPackedSignedAccelerated = false,
      .integerDotProduct4x8BitPackedMixedSignednessAccelerated = false,
      .integerDotProduct16BitUnsignedAccelerated = false,
      .integerDotProduct16BitSignedAccelerated = false,
      .integerDotProduct16BitMixedSignednessAccelerated = false,
      .integerDotProduct32BitUnsignedAccelerated = false,
      .integerDotProduct32BitSignedAccelerated = false,
      .integerDotProduct32BitMixedSignednessAccelerated = false,
      .integerDotProduct64BitUnsignedAccelerated = false,
      .integerDotProduct64BitSignedAccelerated = false,
      .integerDotProduct64BitMixedSignednessAccelerated = false,
      .integerDotProductAccumulatingSaturating8BitUnsignedAccelerated = false,
      .integerDotProductAccumulatingSaturating8BitSignedAccelerated = false,
      .integerDotProductAccumulatingSaturating8BitMixedSignednessAccelerated = false,
      .integerDotProductAccumulatingSaturating4x8BitPackedUnsignedAccelerated = false,
      .integerDotProductAccumulatingSaturating4x8BitPackedSignedAccelerated = false,
      .integerDotProductAccumulatingSaturating4x8BitPackedMixedSignednessAccelerated = false,
      .integerDotProductAccumulatingSaturating16BitUnsignedAccelerated = false,
      .integerDotProductAccumulatingSaturating16BitSignedAccelerated = false,
      .integerDotProductAccumulatingSaturating16BitMixedSignednessAccelerated = false,
      .integerDotProductAccumulatingSaturating32BitUnsignedAccelerated = false,
      .integerDotProductAccumulatingSaturating32BitSignedAccelerated = false,
      .integerDotProductAccumulatingSaturating32BitMixedSignednessAccelerated = false,
      .integerDotProductAccumulatingSaturating64BitUnsignedAccelerated = false,
      .integerDotProductAccumulatingSaturating64BitSignedAccelerated = false,
      .integerDotProductAccumulatingSaturating64BitMixedSignednessAccelerated = false,

      /* Vulkan 1.2 / VK_KHR_timeline_semaphore */
      .maxTimelineSemaphoreValueDifference = UINT64_MAX,

      /* Vulkan 1.3 / VK_EXT_texel_buffer_alignment */
      .storageTexelBufferOffsetAlignmentBytes = PVR_TEXEL_BUFFER_OFFSET_ALIGNMENT,
      .storageTexelBufferOffsetSingleTexelAlignment = true,
      .uniformTexelBufferOffsetAlignmentBytes = PVR_TEXEL_BUFFER_OFFSET_ALIGNMENT,
      .uniformTexelBufferOffsetSingleTexelAlignment = false,

      /* Vulkan 1.3 / VK_KHR_maintenance4 */
      .maxBufferSize = max_memory_alloc_size,

      /* Vulkan 1.4 / VK_EXT_vertex_attribute_divisor / VK_KHR_vertex_attribute_divisor */
      .maxVertexAttribDivisor = UINT32_MAX,
      .supportsNonZeroFirstInstance = true,

      /* VK_EXT_custom_border_color */
      .maxCustomBorderColorSamplers =
         get_custom_border_color_samplers(&pdevice->dev_info),

      /* VK_EXT_physical_device_drm */
      .drmHasPrimary = pdevice->has_primary,
      .drmPrimaryMajor = (int64_t) major(pdevice->primary_devid),
      .drmPrimaryMinor = (int64_t) minor(pdevice->primary_devid),
      .drmHasRender = true,
      .drmRenderMajor = (int64_t) major(pdevice->render_devid),
      .drmRenderMinor = (int64_t) minor(pdevice->render_devid),

      /* Vulkan 1.2 / VK_KHR_depth_stencil_resolve */
      .supportedDepthResolveModes =
         VK_RESOLVE_MODE_SAMPLE_ZERO_BIT,
      .supportedStencilResolveModes =
         VK_RESOLVE_MODE_SAMPLE_ZERO_BIT,
      .independentResolveNone = true,
      .independentResolve = true,

      /* VK_KHR_line_rasterization */
      .lineSubPixelPrecisionBits = line_sub_pixel_precision_bits,
   };

   if (PVR_HAS_FEATURE(dev_info, gpu_multicore_support)) {
      snprintf(properties->deviceName,
               sizeof(properties->deviceName),
               "PowerVR %s %s MC%u",
               dev_info->ident.series_name,
               dev_info->ident.public_name,
               dev_runtime_info->core_count);
   } else {
      snprintf(properties->deviceName,
               sizeof(properties->deviceName),
               "PowerVR %s %s",
               dev_info->ident.series_name,
               dev_info->ident.public_name);
   }

   return true;
}

static bool pvr_physical_device_setup_pipeline_cache(
   struct pvr_physical_device *const pdevice)
{
#ifdef ENABLE_SHADER_CACHE
   const struct pvr_instance *instance = pdevice->instance;
   char device_id[BLAKE3_KEY_LEN * 2 + 1];
   char driver_id[BLAKE3_KEY_LEN * 2 + 1];

   _mesa_blake3_format(device_id, pdevice->device_uuid);
   _mesa_blake3_format(driver_id, instance->driver_build_sha);

   pdevice->vk.disk_cache = disk_cache_create(device_id, driver_id, 0U);
   return !!pdevice->vk.disk_cache;
#else
   return true;
#endif /* ENABLE_SHADER_CACHE */
}

static void
pvr_get_device_uuid(const struct pvr_device_info *dev_info,
                    uint8_t uuid_out[const static BLAKE3_KEY_LEN])
{
   uint64_t bvnc = pvr_get_packed_bvnc(dev_info);
   static const char *device_str = "pvr";
   blake3_hasher blake3_ctx;

   _mesa_blake3_init(&blake3_ctx);
   _mesa_blake3_update(&blake3_ctx, device_str, strlen(device_str));
   _mesa_blake3_update(&blake3_ctx, &bvnc, sizeof(bvnc));
   _mesa_blake3_final(&blake3_ctx, uuid_out);
}

static void
pvr_get_cache_uuid(const struct pvr_physical_device *const pdevice,
                   uint8_t uuid_out[const static BLAKE3_KEY_LEN])
{
   const struct pvr_instance *instance = pdevice->instance;
   static const char *cache_str = "cache";
   blake3_hasher blake3_ctx;

   _mesa_blake3_init(&blake3_ctx);
   _mesa_blake3_update(&blake3_ctx, cache_str, strlen(cache_str));
   _mesa_blake3_update(&blake3_ctx,
                     pdevice->device_uuid,
                     sizeof(pdevice->device_uuid));
   _mesa_blake3_update(&blake3_ctx,
                     instance->driver_build_sha,
                     sizeof(instance->driver_build_sha));
   _mesa_blake3_final(&blake3_ctx, uuid_out);
}

static void
pvr_physical_device_setup_uuids(struct pvr_physical_device *const pdevice)
{
   const struct pvr_instance *instance = pdevice->instance;

   pvr_get_device_uuid(&pdevice->dev_info, pdevice->device_uuid);
   pvr_get_cache_uuid(pdevice, pdevice->cache_uuid);

   memcpy(pdevice->vk.properties.driverUUID,
          instance->driver_build_sha,
          sizeof(pdevice->vk.properties.driverUUID));

   memcpy(pdevice->vk.properties.deviceUUID,
          pdevice->device_uuid,
          sizeof(pdevice->vk.properties.deviceUUID));

   memcpy(pdevice->vk.properties.pipelineCacheUUID,
          pdevice->cache_uuid,
          sizeof(pdevice->vk.properties.pipelineCacheUUID));

   memcpy(pdevice->vk.properties.shaderBinaryUUID,
          pdevice->cache_uuid,
          sizeof(pdevice->vk.properties.shaderBinaryUUID));
}

static bool pvr_device_is_conformant(const struct pvr_device_info *info)
{
   const uint64_t bvnc = pvr_get_packed_bvnc(info);
   switch (bvnc) {
   case PVR_BVNC_PACK(36, 53, 104, 796):
      return true;

   default:
      break;
   }

   return false;
}

/* Minimum required by the Vulkan 1.1 spec (see Table 32. Required Limits) */
#define PVR_MAX_MEMORY_ALLOCATION_SIZE (1ull << 30)

static uint64_t pvr_compute_heap_size(void)
{
   /* Query the total ram from the system */
   uint64_t total_ram;
   if (!os_get_total_physical_memory(&total_ram))
      return 0;

   if (total_ram < PVR_MAX_MEMORY_ALLOCATION_SIZE) {
      mesa_logw(
         "Warning: The available RAM is below the minimum required by the Vulkan specification!");
   }

   /* We don't want to burn too much ram with the GPU. If the user has 4GiB
    * or less, we use at most half. If they have more than 4GiB, we use 3/4.
    */
   uint64_t available_ram;
   if (total_ram <= 4ULL * 1024ULL * 1024ULL * 1024ULL)
      available_ram = total_ram / 2U;
   else
      available_ram = total_ram * 3U / 4U;

   return MAX2(available_ram, PVR_MAX_MEMORY_ALLOCATION_SIZE);
}

static void
pvr_physical_device_setup_formats(struct pvr_physical_device *const pdevice);

VkResult pvr_physical_device_init(struct pvr_physical_device *pdevice,
                                  struct pvr_instance *instance,
                                  drmDevicePtr drm_render_device,
                                  drmDevicePtr drm_display_device)
{
   struct vk_physical_device_dispatch_table dispatch_table;
   struct vk_device_extension_table supported_extensions;
   struct vk_properties supported_properties;
   struct vk_features supported_features;
   struct pvr_winsys *ws;
   struct stat primary_stat = { 0 }, render_stat = { 0 };
   char *primary_path;
   char *display_path;
   char *render_path;
   VkResult result;

   render_path = vk_strdup(&instance->vk.alloc,
                           drm_render_device->nodes[DRM_NODE_RENDER],
                           VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
   if (!render_path) {
      result = VK_ERROR_OUT_OF_HOST_MEMORY;
      goto err_out;
   }

   if (instance->vk.enabled_extensions.KHR_display && drm_display_device) {
      display_path = vk_strdup(&instance->vk.alloc,
                               drm_display_device->nodes[DRM_NODE_PRIMARY],
                               VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
      if (!display_path) {
         result = VK_ERROR_OUT_OF_HOST_MEMORY;
         goto err_vk_free_render_path;
      }
   } else {
      display_path = NULL;
   }

   primary_path = drm_render_device->nodes[DRM_NODE_PRIMARY];
   pdevice->has_primary = false;
   pdevice->primary_devid = 0;
   if (!stat(primary_path, &primary_stat)) {
      pdevice->has_primary = true;
      pdevice->primary_devid = primary_stat.st_rdev;
   }

   if (stat(render_path, &render_stat) != 0) {
      result = vk_errorf(instance,
                         VK_ERROR_INITIALIZATION_FAILED,
                         "failed to stat DRM render node %s",
                         render_path);
      goto err_vk_free_display_path;
   }
   pdevice->render_devid = render_stat.st_rdev;

   result =
      pvr_winsys_create(render_path, display_path, true,
                        &instance->vk.alloc, &ws);
   if (result != VK_SUCCESS)
      goto err_vk_free_display_path;

   pdevice->instance = instance;
   pdevice->render_path = render_path;
   pdevice->display_path = display_path;
   pdevice->ws = ws;

   result = ws->ops->device_info_init(ws,
                                      &pdevice->dev_info,
                                      &pdevice->dev_runtime_info);
   if (result != VK_SUCCESS)
      goto err_pvr_winsys_destroy;

   if (!pvr_device_is_conformant(&pdevice->dev_info)) {
      if (!os_get_option("PVR_I_WANT_A_BROKEN_VULKAN_DRIVER")) {
         result = vk_errorf(instance,
                            VK_ERROR_INCOMPATIBLE_DRIVER,
                            "WARNING: powervr is not a conformant Vulkan "
                            "implementation for %s. Pass "
                            "PVR_I_WANT_A_BROKEN_VULKAN_DRIVER=1 if you know "
                            "what you're doing.",
                            pdevice->dev_info.ident.public_name);
         goto err_pvr_winsys_destroy;
      }

      vk_warn_non_conformant_implementation("powervr");
   }

   /* Setup available memory heaps and types */
   pdevice->memory.memoryHeapCount = 1;
   pdevice->memory.memoryHeaps[0].size = pvr_compute_heap_size();
   pdevice->memory.memoryHeaps[0].flags = VK_MEMORY_HEAP_DEVICE_LOCAL_BIT;

   pdevice->memory.memoryTypeCount = 1;
   pdevice->memory.memoryTypes[0].propertyFlags =
      VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
      VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
      VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
   pdevice->memory.memoryTypes[0].heapIndex = 0;

   pvr_physical_device_get_supported_extensions(&supported_extensions);
   pvr_physical_device_get_supported_features(&pdevice->dev_info,
                                              &supported_features);
   if (!pvr_physical_device_get_properties(pdevice, &supported_properties)) {
      result = vk_errorf(instance,
                         VK_ERROR_INITIALIZATION_FAILED,
                         "Failed to collect physical device properties");
      goto err_pvr_winsys_destroy;
   }

   vk_physical_device_dispatch_table_from_entrypoints(
      &dispatch_table,
      &pvr_physical_device_entrypoints,
      true);

   vk_physical_device_dispatch_table_from_entrypoints(
      &dispatch_table,
      &wsi_physical_device_entrypoints,
      false);

   result = vk_physical_device_init(&pdevice->vk,
                                    &instance->vk,
                                    &supported_extensions,
                                    &supported_features,
                                    &supported_properties,
                                    &dispatch_table);
   if (result != VK_SUCCESS)
      goto err_pvr_winsys_destroy;

   pvr_physical_device_setup_formats(pdevice);

   pvr_physical_device_setup_uuids(pdevice);

   if (!pvr_physical_device_setup_pipeline_cache(pdevice)) {
      result = vk_errorf(NULL,
                         VK_ERROR_INITIALIZATION_FAILED,
                         "Failed to get driver build sha.");
      goto err_vk_physical_device_finish;
   }

   pdevice->vk.supported_sync_types = ws->sync_types;

   pdevice->pco_ctx = pco_ctx_create(&pdevice->dev_info, NULL);
   if (!pdevice->pco_ctx) {
      result = vk_errorf(instance,
                         VK_ERROR_INITIALIZATION_FAILED,
                         "Failed to initialize PCO compiler context");
      goto err_free_pipeline_cache;
   }
   pco_ctx_setup_usclib(pdevice->pco_ctx,
                        pco_usclib_0_nir,
                        sizeof(pco_usclib_0_nir));

   result = pvr_wsi_init(pdevice);
   if (result != VK_SUCCESS) {
      vk_error(instance, result);
      goto err_free_pco_ctx;
   }

   return VK_SUCCESS;

err_free_pco_ctx:
   ralloc_free(pdevice->pco_ctx);

err_free_pipeline_cache:
   pvr_physical_device_free_pipeline_cache(pdevice);

err_vk_physical_device_finish:
   vk_physical_device_finish(&pdevice->vk);

err_pvr_winsys_destroy:
   pvr_winsys_destroy(ws);

err_vk_free_display_path:
   vk_free(&instance->vk.alloc, display_path);

err_vk_free_render_path:
   vk_free(&instance->vk.alloc, render_path);

err_out:
   return result;
}

const static VkQueueFamilyProperties pvr_queue_family_properties = {
   .queueFlags = VK_QUEUE_COMPUTE_BIT | VK_QUEUE_GRAPHICS_BIT |
                 VK_QUEUE_TRANSFER_BIT,
   .queueCount = PVR_MAX_QUEUES,
   .timestampValidBits = 0,
   .minImageTransferGranularity = { 1, 1, 1 },
};

void pvr_GetPhysicalDeviceQueueFamilyProperties2(
   VkPhysicalDevice physicalDevice,
   uint32_t *pQueueFamilyPropertyCount,
   VkQueueFamilyProperties2 *pQueueFamilyProperties)
{
   VK_OUTARRAY_MAKE_TYPED(VkQueueFamilyProperties2,
                          out,
                          pQueueFamilyProperties,
                          pQueueFamilyPropertyCount);

   vk_outarray_append_typed (VkQueueFamilyProperties2, &out, p) {
      p->queueFamilyProperties = pvr_queue_family_properties;

      vk_foreach_struct (ext, p->pNext) {
         vk_debug_ignored_stype(ext->sType);
      }
   }
}

void pvr_GetPhysicalDeviceMemoryProperties2(
   VkPhysicalDevice physicalDevice,
   VkPhysicalDeviceMemoryProperties2 *pMemoryProperties)
{
   VK_FROM_HANDLE(pvr_physical_device, pdevice, physicalDevice);

   pMemoryProperties->memoryProperties = pdevice->memory;

   vk_foreach_struct (ext, pMemoryProperties->pNext) {
      vk_debug_ignored_stype(ext->sType);
   }
}

#define PER_ARCH_FUNCS(arch)                                                  \
   VkResult pvr_##arch##_create_device(                                       \
      struct pvr_physical_device *physical_device,                            \
      const VkDeviceCreateInfo *pCreateInfo,                                  \
      const VkAllocationCallbacks *pAllocator,                                \
      VkDevice *pDevice);                                                     \
                                                                              \
   void pvr_##arch##_destroy_device(struct pvr_device *device,                \
                                    const VkAllocationCallbacks *pAllocator); \
                                                                              \
   struct pvr_format_table pvr_##arch##_get_format_table(void)

PER_ARCH_FUNCS(rogue);

VkResult pvr_CreateDevice(VkPhysicalDevice physicalDevice,
                          const VkDeviceCreateInfo *pCreateInfo,
                          const VkAllocationCallbacks *pAllocator,
                          VkDevice *pDevice)
{
   VK_FROM_HANDLE(pvr_physical_device, pdevice, physicalDevice);

   enum pvr_device_arch arch = pdevice->dev_info.ident.arch;
   VkResult result = VK_ERROR_INITIALIZATION_FAILED;
   PVR_ARCH_DISPATCH_RET(create_device,
                         arch,
                         result,
                         pdevice,
                         pCreateInfo,
                         pAllocator,
                         pDevice);

   return result;
}

void pvr_DestroyDevice(VkDevice _device,
                       const VkAllocationCallbacks *pAllocator)
{
   VK_FROM_HANDLE(pvr_device, device, _device);

   enum pvr_device_arch arch = device->pdevice->dev_info.ident.arch;
   PVR_ARCH_DISPATCH(destroy_device, arch, device, pAllocator);
}

static void
pvr_physical_device_setup_formats(struct pvr_physical_device *const pdevice)
{
   enum pvr_device_arch arch = pdevice->dev_info.ident.arch;
   PVR_ARCH_DISPATCH_RET(get_format_table, arch, pdevice->formats);
}

/* Leave this at the very end, to avoid leakage of HW-defs here */
#define PVR_BUILD_ARCH_ROGUE
#include "pvr_border.h"

static unsigned
get_custom_border_color_samplers(const struct pvr_device_info *dev_info)
{
   assert(dev_info->ident.arch == PVR_DEVICE_ARCH_ROGUE);
   return PVR_BORDER_COLOR_TABLE_NR_CUSTOM_ENTRIES;
}
