pvr: Minor refactor of pvr_device.c
authorMatt Coster <matt.coster@imgtec.com>
Tue, 15 Aug 2023 09:45:27 +0000 (10:45 +0100)
committerMarge Bot <emma+marge@anholt.net>
Fri, 13 Oct 2023 12:21:18 +0000 (12:21 +0000)
Moving a few functions further up here to prepare for the next commit;
should make the diffs a lot nicer. No (intentional) functional changes.

Signed-off-by: Matt Coster <matt.coster@imgtec.com>
Reviewed-by: Karmjit Mahil <Karmjit.Mahil@imgtec.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/25326>

src/imagination/vulkan/pvr_device.c

index 75a0786..5578756 100644 (file)
@@ -247,68 +247,6 @@ static void pvr_physical_device_get_supported_features(
    };
 }
 
-VkResult pvr_EnumerateInstanceVersion(uint32_t *pApiVersion)
-{
-   *pApiVersion = PVR_API_VERSION;
-   return VK_SUCCESS;
-}
-
-VkResult
-pvr_EnumerateInstanceExtensionProperties(const char *pLayerName,
-                                         uint32_t *pPropertyCount,
-                                         VkExtensionProperties *pProperties)
-{
-   if (pLayerName)
-      return vk_error(NULL, VK_ERROR_LAYER_NOT_PRESENT);
-
-   return vk_enumerate_instance_extension_properties(&pvr_instance_extensions,
-                                                     pPropertyCount,
-                                                     pProperties);
-}
-
-static 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.
-    */
-
-   if (pdevice->compiler)
-      ralloc_free(pdevice->compiler);
-
-   pvr_wsi_finish(pdevice);
-
-   free(pdevice->name);
-
-   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_DestroyInstance(VkInstance _instance,
-                         const VkAllocationCallbacks *pAllocator)
-{
-   PVR_FROM_HANDLE(pvr_instance, instance, _instance);
-
-   if (!instance)
-      return;
-
-   VG(VALGRIND_DESTROY_MEMPOOL(instance));
-
-   vk_instance_finish(&instance->vk);
-   vk_free(&instance->vk.alloc, instance);
-}
-
 static VkResult
 pvr_physical_device_init_uuids(struct pvr_physical_device *pdevice)
 {
@@ -343,887 +281,949 @@ pvr_physical_device_init_uuids(struct pvr_physical_device *pdevice)
    return VK_SUCCESS;
 }
 
-static uint64_t pvr_compute_heap_size(void)
+struct pvr_descriptor_limits {
+   uint32_t max_per_stage_resources;
+   uint32_t max_per_stage_samplers;
+   uint32_t max_per_stage_uniform_buffers;
+   uint32_t max_per_stage_storage_buffers;
+   uint32_t max_per_stage_sampled_images;
+   uint32_t max_per_stage_storage_images;
+   uint32_t max_per_stage_input_attachments;
+};
+
+static const struct pvr_descriptor_limits *
+pvr_get_physical_device_descriptor_limits(
+   const struct pvr_device_info *dev_info,
+   const struct pvr_device_runtime_info *dev_runtime_info)
 {
-   /* Query the total ram from the system */
-   uint64_t total_ram;
-   if (!os_get_total_physical_memory(&total_ram))
-      return 0;
+   enum pvr_descriptor_cs_level {
+      /* clang-format off */
+      CS4096, /* 6XT and some XE cores with large CS. */
+      CS2560, /* Mid range Rogue XE cores. */
+      CS2048, /* Low end Rogue XE cores. */
+      CS1536, /* Ultra-low-end 9XEP. */
+      CS680,  /* lower limits for older devices. */
+      CS408,  /* 7XE. */
+      /* clang-format on */
+   };
 
-   /* 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;
+   static const struct pvr_descriptor_limits descriptor_limits[] = {
+      [CS4096] = { 1160U, 256U, 192U, 144U, 256U, 256U, 8U, },
+      [CS2560] = {  648U, 128U, 128U, 128U, 128U, 128U, 8U, },
+      [CS2048] = {  584U, 128U,  96U,  64U, 128U, 128U, 8U, },
+      [CS1536] = {  456U,  64U,  96U,  64U, 128U,  64U, 8U, },
+      [CS680]  = {  224U,  32U,  64U,  36U,  48U,   8U, 8U, },
+      [CS408]  = {  128U,  16U,  40U,  28U,  16U,   8U, 8U, },
+   };
 
-   return available_ram;
+   const uint32_t common_size =
+      pvr_calc_fscommon_size_and_tiles_in_flight(dev_info,
+                                                 dev_runtime_info,
+                                                 UINT32_MAX,
+                                                 1);
+   enum pvr_descriptor_cs_level cs_level;
+
+   if (common_size >= 2048) {
+      cs_level = CS2048;
+   } else if (common_size >= 1526) {
+      cs_level = CS1536;
+   } else if (common_size >= 680) {
+      cs_level = CS680;
+   } else if (common_size >= 408) {
+      cs_level = CS408;
+   } else {
+      mesa_loge("This core appears to have a very limited amount of shared "
+                "register space and may not meet the Vulkan spec limits.");
+      abort();
+   }
+
+   return &descriptor_limits[cs_level];
 }
 
-static VkResult pvr_physical_device_init(struct pvr_physical_device *pdevice,
-                                         struct pvr_instance *instance,
-                                         drmDevicePtr drm_render_device,
-                                         drmDevicePtr drm_display_device)
+static void
+pvr_get_physical_device_properties_1_1(struct pvr_physical_device *pdevice,
+                                       VkPhysicalDeviceVulkan11Properties *p)
 {
-   struct vk_physical_device_dispatch_table dispatch_table;
-   struct vk_device_extension_table supported_extensions;
-   struct vk_features supported_features;
-   struct pvr_winsys *ws;
-   char *display_path;
-   char *render_path;
-   VkResult result;
+   assert(p->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES);
+}
 
-   if (!getenv("PVR_I_WANT_A_BROKEN_VULKAN_DRIVER")) {
-      return vk_errorf(instance,
-                       VK_ERROR_INCOMPATIBLE_DRIVER,
-                       "WARNING: powervr is not a conformant Vulkan "
-                       "implementation. Pass "
-                       "PVR_I_WANT_A_BROKEN_VULKAN_DRIVER=1 if you know "
-                       "what you're doing.");
-   }
+static void
+pvr_get_physical_device_properties_1_2(struct pvr_physical_device *pdevice,
+                                       VkPhysicalDeviceVulkan12Properties *p)
+{
+   assert(p->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_PROPERTIES);
 
-   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;
-   }
+   /* VK_KHR_driver_properties */
+   p->driverID = VK_DRIVER_ID_IMAGINATION_OPEN_SOURCE_MESA;
+   memset(p->driverName, 0, sizeof(p->driverName));
+   snprintf(p->driverName,
+            VK_MAX_DRIVER_NAME_SIZE,
+            "Imagination open-source Mesa driver");
+   memset(p->driverInfo, 0, sizeof(p->driverInfo));
+   snprintf(p->driverInfo,
+            VK_MAX_DRIVER_INFO_SIZE,
+            ("Mesa " PACKAGE_VERSION MESA_GIT_SHA1));
+   p->conformanceVersion = (VkConformanceVersion){
+      .major = 1,
+      .minor = 3,
+      .subminor = 4,
+      .patch = 1,
+   };
 
-   if (instance->vk.enabled_extensions.KHR_display) {
-      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;
-   }
+   /* VK_KHR_timeline_semaphore */
+   p->maxTimelineSemaphoreValueDifference = UINT64_MAX;
+}
 
-   result =
-      pvr_winsys_create(render_path, display_path, &instance->vk.alloc, &ws);
-   if (result != VK_SUCCESS)
-      goto err_vk_free_display_path;
+static void
+pvr_get_physical_device_properties_1_3(struct pvr_physical_device *pdevice,
+                                       VkPhysicalDeviceVulkan13Properties *p)
+{
+   assert(p->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_PROPERTIES);
+}
 
-   pdevice->instance = instance;
-   pdevice->render_path = render_path;
-   pdevice->display_path = display_path;
-   pdevice->ws = ws;
+void pvr_GetPhysicalDeviceProperties(VkPhysicalDevice physicalDevice,
+                                     VkPhysicalDeviceProperties *pProperties)
+{
+   PVR_FROM_HANDLE(pvr_physical_device, pdevice, physicalDevice);
+   const struct pvr_device_info *const dev_info = &pdevice->dev_info;
 
-   result = ws->ops->device_info_init(ws,
-                                      &pdevice->dev_info,
-                                      &pdevice->dev_runtime_info);
-   if (result != VK_SUCCESS)
-      goto err_pvr_winsys_destroy;
+   const struct pvr_descriptor_limits *descriptor_limits =
+      pvr_get_physical_device_descriptor_limits(dev_info,
+                                                &pdevice->dev_runtime_info);
 
-   pvr_physical_device_get_supported_extensions(&supported_extensions);
-   pvr_physical_device_get_supported_features(&pdevice->dev_info,
-                                              &supported_features);
+   /* 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);
 
-   vk_physical_device_dispatch_table_from_entrypoints(
-      &dispatch_table,
-      &pvr_physical_device_entrypoints,
-      true);
+   /* Default value based on the minimum value found in all existing cores. */
+   const uint32_t uvs_banks = PVR_GET_FEATURE_VALUE(dev_info, uvs_banks, 2);
 
-   vk_physical_device_dispatch_table_from_entrypoints(
-      &dispatch_table,
-      &wsi_physical_device_entrypoints,
-      false);
+   /* Default value based on the minimum value found in all existing cores. */
+   const uint32_t uvs_pba_entries =
+      PVR_GET_FEATURE_VALUE(dev_info, uvs_pba_entries, 160);
 
-   result = vk_physical_device_init(&pdevice->vk,
-                                    &instance->vk,
-                                    &supported_extensions,
-                                    &supported_features,
-                                    NULL,
-                                    &dispatch_table);
-   if (result != VK_SUCCESS)
-      goto err_pvr_winsys_destroy;
+   /* Default value based on the minimum value found in all existing cores. */
+   const uint32_t num_user_clip_planes =
+      PVR_GET_FEATURE_VALUE(dev_info, num_user_clip_planes, 8);
 
-   pdevice->vk.supported_sync_types = ws->sync_types;
+   const uint32_t sub_pixel_precision =
+      PVR_HAS_FEATURE(dev_info, simple_internal_parameter_format) ? 4U : 8U;
 
-   result = pvr_physical_device_init_uuids(pdevice);
-   if (result != VK_SUCCESS)
-      goto err_vk_physical_device_finish;
-
-   if (asprintf(&pdevice->name,
-                "Imagination PowerVR %s %s",
-                pdevice->dev_info.ident.series_name,
-                pdevice->dev_info.ident.public_name) < 0) {
-      result = vk_errorf(instance,
-                         VK_ERROR_OUT_OF_HOST_MEMORY,
-                         "Unable to allocate memory to store device name");
-      goto err_vk_physical_device_finish;
-   }
-
-   /* 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;
+   const uint32_t max_render_size = rogue_get_render_size_max(dev_info);
 
-   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;
+   const uint32_t max_sample_bits = ((max_multisample << 1) - 1);
 
-   result = pvr_wsi_init(pdevice);
-   if (result != VK_SUCCESS) {
-      vk_error(instance, result);
-      goto err_free_name;
-   }
+   const uint32_t max_user_vertex_components =
+      ((uvs_banks <= 8U) && (uvs_pba_entries == 160U)) ? 64U : 128U;
 
-   pdevice->compiler = rogue_compiler_create(&pdevice->dev_info);
-   if (!pdevice->compiler) {
-      result = vk_errorf(instance,
-                         VK_ERROR_INITIALIZATION_FAILED,
-                         "Failed to initialize Rogue compiler");
-      goto err_wsi_finish;
-   }
+   /* 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
+    */
 
-   return VK_SUCCESS;
+   /* 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);
 
-err_wsi_finish:
-   pvr_wsi_finish(pdevice);
+   /* 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);
 
-err_free_name:
-   free(pdevice->name);
+   const uint32_t max_compute_work_group_invocations =
+      (usc_slots * max_instances_per_pds_task >= 512U) ? 512U : 384U;
 
-err_vk_physical_device_finish:
-   vk_physical_device_finish(&pdevice->vk);
+   VkPhysicalDeviceLimits limits = {
+      .maxImageDimension1D = max_render_size,
+      .maxImageDimension2D = max_render_size,
+      .maxImageDimension3D = PVR_MAX_TEXTURE_EXTENT_Z,
+      .maxImageDimensionCube = max_render_size,
+      .maxImageArrayLayers = PVR_MAX_ARRAY_LAYERS,
+      .maxTexelBufferElements = 64U * 1024U,
+      .maxUniformBufferRange = 128U * 1024U * 1024U,
+      .maxStorageBufferRange = 128U * 1024U * 1024U,
+      .maxPushConstantsSize = PVR_MAX_PUSH_CONSTANTS_SIZE,
+      .maxMemoryAllocationCount = UINT32_MAX,
+      .maxSamplerAllocationCount = UINT32_MAX,
+      .bufferImageGranularity = 1U,
+      .sparseAddressSpaceSize = 256ULL * 1024ULL * 1024ULL * 1024ULL,
 
-err_pvr_winsys_destroy:
-   pvr_winsys_destroy(ws);
+      /* Maximum number of descriptor sets that can be bound at the same time.
+       */
+      .maxBoundDescriptorSets = PVR_MAX_DESCRIPTOR_SETS,
 
-err_vk_free_display_path:
-   vk_free(&instance->vk.alloc, display_path);
+      .maxPerStageResources = descriptor_limits->max_per_stage_resources,
+      .maxPerStageDescriptorSamplers =
+         descriptor_limits->max_per_stage_samplers,
+      .maxPerStageDescriptorUniformBuffers =
+         descriptor_limits->max_per_stage_uniform_buffers,
+      .maxPerStageDescriptorStorageBuffers =
+         descriptor_limits->max_per_stage_storage_buffers,
+      .maxPerStageDescriptorSampledImages =
+         descriptor_limits->max_per_stage_sampled_images,
+      .maxPerStageDescriptorStorageImages =
+         descriptor_limits->max_per_stage_storage_images,
+      .maxPerStageDescriptorInputAttachments =
+         descriptor_limits->max_per_stage_input_attachments,
 
-err_vk_free_render_path:
-   vk_free(&instance->vk.alloc, render_path);
+      .maxDescriptorSetSamplers = 256U,
+      .maxDescriptorSetUniformBuffers = 256U,
+      .maxDescriptorSetUniformBuffersDynamic =
+         PVR_MAX_DESCRIPTOR_SET_UNIFORM_DYNAMIC_BUFFERS,
+      .maxDescriptorSetStorageBuffers = 256U,
+      .maxDescriptorSetStorageBuffersDynamic =
+         PVR_MAX_DESCRIPTOR_SET_STORAGE_DYNAMIC_BUFFERS,
+      .maxDescriptorSetSampledImages = 256U,
+      .maxDescriptorSetStorageImages = 256U,
+      .maxDescriptorSetInputAttachments = 256U,
 
-err_out:
-   return result;
-}
+      /* Vertex Shader Limits */
+      .maxVertexInputAttributes = PVR_MAX_VERTEX_INPUT_BINDINGS,
+      .maxVertexInputBindings = PVR_MAX_VERTEX_INPUT_BINDINGS,
+      .maxVertexInputAttributeOffset = 0xFFFF,
+      .maxVertexInputBindingStride = 1024U * 1024U * 1024U * 2U,
+      .maxVertexOutputComponents = max_user_vertex_components,
 
-static VkResult pvr_get_drm_devices(void *const obj,
-                                    drmDevicePtr *const devices,
-                                    const int max_devices,
-                                    int *const num_devices_out)
-{
-   int ret = drmGetDevices2(0, devices, max_devices);
-   if (ret < 0) {
-      return vk_errorf(obj,
-                       VK_ERROR_INITIALIZATION_FAILED,
-                       "Failed to enumerate drm devices (errno %d: %s)",
-                       -ret,
-                       strerror(-ret));
-   }
+      /* Tessellation Limits */
+      .maxTessellationGenerationLevel = 0,
+      .maxTessellationPatchSize = 0,
+      .maxTessellationControlPerVertexInputComponents = 0,
+      .maxTessellationControlPerVertexOutputComponents = 0,
+      .maxTessellationControlPerPatchOutputComponents = 0,
+      .maxTessellationControlTotalOutputComponents = 0,
+      .maxTessellationEvaluationInputComponents = 0,
+      .maxTessellationEvaluationOutputComponents = 0,
 
-   if (num_devices_out)
-      *num_devices_out = ret;
+      /* Geometry Shader Limits */
+      .maxGeometryShaderInvocations = 0,
+      .maxGeometryInputComponents = 0,
+      .maxGeometryOutputComponents = 0,
+      .maxGeometryOutputVertices = 0,
+      .maxGeometryTotalOutputComponents = 0,
 
-   return VK_SUCCESS;
-}
+      /* Fragment Shader Limits */
+      .maxFragmentInputComponents = max_user_vertex_components,
+      .maxFragmentOutputAttachments = PVR_MAX_COLOR_ATTACHMENTS,
+      .maxFragmentDualSrcAttachments = 0,
+      .maxFragmentCombinedOutputResources =
+         descriptor_limits->max_per_stage_storage_buffers +
+         descriptor_limits->max_per_stage_storage_images +
+         PVR_MAX_COLOR_ATTACHMENTS,
 
-static bool
-pvr_drm_device_compatible(const struct pvr_drm_device_info *const info,
-                          drmDevice *const drm_dev)
-{
-   char **const compatible = drm_dev->deviceinfo.platform->compatible;
+      /* Compute Shader Limits */
+      .maxComputeSharedMemorySize = 16U * 1024U,
+      .maxComputeWorkGroupCount = { 64U * 1024U, 64U * 1024U, 64U * 1024U },
+      .maxComputeWorkGroupInvocations = max_compute_work_group_invocations,
+      .maxComputeWorkGroupSize = { max_compute_work_group_invocations,
+                                   max_compute_work_group_invocations,
+                                   64U },
 
-   for (char **compat = compatible; *compat; compat++) {
-      if (strncmp(*compat, info->name, info->len) == 0)
-         return true;
-   }
+      /* Rasterization Limits */
+      .subPixelPrecisionBits = sub_pixel_precision,
+      .subTexelPrecisionBits = 8U,
+      .mipmapPrecisionBits = 8U,
 
-   return false;
-}
+      .maxDrawIndexedIndexValue = UINT32_MAX,
+      .maxDrawIndirectCount = 2U * 1024U * 1024U * 1024U,
+      .maxSamplerLodBias = 16.0f,
+      .maxSamplerAnisotropy = 1.0f,
+      .maxViewports = PVR_MAX_VIEWPORTS,
 
-static const struct pvr_drm_device_config *
-pvr_drm_device_get_config(drmDevice *const drm_dev)
-{
-   for (size_t i = 0U; i < ARRAY_SIZE(pvr_drm_configs); i++) {
-      if (pvr_drm_device_compatible(&pvr_drm_configs[i].render, drm_dev))
-         return &pvr_drm_configs[i];
-   }
+      .maxViewportDimensions[0] = max_render_size,
+      .maxViewportDimensions[1] = max_render_size,
+      .viewportBoundsRange[0] = -(int32_t)(2U * max_render_size),
+      .viewportBoundsRange[1] = 2U * max_render_size,
 
-   return NULL;
-}
+      .viewportSubPixelBits = 0,
+      .minMemoryMapAlignment = 64U,
+      .minTexelBufferOffsetAlignment = 16U,
+      .minUniformBufferOffsetAlignment = 4U,
+      .minStorageBufferOffsetAlignment = 4U,
 
-static void
-pvr_physical_device_dump_info(const struct pvr_physical_device *pdevice,
-                              char *const *comp_display,
-                              char *const *comp_render)
-{
-   drmVersionPtr version_display, version_render;
-   struct pvr_device_dump_info info;
+      .minTexelOffset = -8,
+      .maxTexelOffset = 7U,
+      .minTexelGatherOffset = -8,
+      .maxTexelGatherOffset = 7,
+      .minInterpolationOffset = -0.5,
+      .maxInterpolationOffset = 0.5,
+      .subPixelInterpolationOffsetBits = 4U,
 
-   version_display = drmGetVersion(pdevice->ws->display_fd);
-   if (!version_display)
-      return;
+      .maxFramebufferWidth = max_render_size,
+      .maxFramebufferHeight = max_render_size,
+      .maxFramebufferLayers = PVR_MAX_FRAMEBUFFER_LAYERS,
 
-   version_render = drmGetVersion(pdevice->ws->render_fd);
-   if (!version_render) {
-      drmFreeVersion(version_display);
-      return;
-   }
+      .framebufferColorSampleCounts = max_sample_bits,
+      .framebufferDepthSampleCounts = max_sample_bits,
+      .framebufferStencilSampleCounts = max_sample_bits,
+      .framebufferNoAttachmentsSampleCounts = max_sample_bits,
+      .maxColorAttachments = PVR_MAX_COLOR_ATTACHMENTS,
+      .sampledImageColorSampleCounts = max_sample_bits,
+      .sampledImageIntegerSampleCounts = max_sample_bits,
+      .sampledImageDepthSampleCounts = max_sample_bits,
+      .sampledImageStencilSampleCounts = max_sample_bits,
+      .storageImageSampleCounts = max_sample_bits,
+      .maxSampleMaskWords = 1U,
+      .timestampComputeAndGraphics = false,
+      .timestampPeriod = 0.0f,
+      .maxClipDistances = num_user_clip_planes,
+      .maxCullDistances = num_user_clip_planes,
+      .maxCombinedClipAndCullDistances = num_user_clip_planes,
+      .discreteQueuePriorities = 2U,
+      .pointSizeRange[0] = 1.0f,
+      .pointSizeRange[1] = 511.0f,
+      .pointSizeGranularity = 0.0625f,
+      .lineWidthRange[0] = 1.0f / 16.0f,
+      .lineWidthRange[1] = 16.0f,
+      .lineWidthGranularity = 1.0f / 16.0f,
+      .strictLines = false,
+      .standardSampleLocations = true,
+      .optimalBufferCopyOffsetAlignment = 4U,
+      .optimalBufferCopyRowPitchAlignment = 4U,
+      .nonCoherentAtomSize = 1U,
+   };
 
-   info.device_info = &pdevice->dev_info;
-   info.device_runtime_info = &pdevice->dev_runtime_info;
-   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;
+   *pProperties = (VkPhysicalDeviceProperties){
+      .apiVersion = PVR_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,
+      .limits = limits,
+      .sparseProperties = { 0 },
+   };
 
-   pvr_dump_physical_device_info(&info);
+   snprintf(pProperties->deviceName,
+            sizeof(pProperties->deviceName),
+            "%s",
+            pdevice->name);
 
-   drmFreeVersion(version_display);
-   drmFreeVersion(version_render);
+   memcpy(pProperties->pipelineCacheUUID,
+          pdevice->pipeline_cache_uuid,
+          VK_UUID_SIZE);
 }
 
-static VkResult
-pvr_physical_device_enumerate(struct vk_instance *const vk_instance)
+void pvr_GetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevice,
+                                      VkPhysicalDeviceProperties2 *pProperties)
 {
-   struct pvr_instance *const instance =
-      container_of(vk_instance, struct pvr_instance, vk);
-
-   const struct pvr_drm_device_config *config = NULL;
-
-   drmDevicePtr drm_display_device = NULL;
-   drmDevicePtr drm_render_device = NULL;
-   struct pvr_physical_device *pdevice;
-   drmDevicePtr *drm_devices;
-   int num_drm_devices = 0;
-   VkResult result;
+   PVR_FROM_HANDLE(pvr_physical_device, pdevice, physicalDevice);
 
-   result = pvr_get_drm_devices(instance, NULL, 0, &num_drm_devices);
-   if (result != VK_SUCCESS)
-      goto out;
+   VkPhysicalDeviceVulkan11Properties core_1_1 = {
+      .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES,
+   };
 
-   if (num_drm_devices == 0) {
-      result = VK_SUCCESS;
-      goto out;
-   }
+   VkPhysicalDeviceVulkan12Properties core_1_2 = {
+      .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_PROPERTIES,
+   };
 
-   drm_devices = vk_alloc(&vk_instance->alloc,
-                          sizeof(*drm_devices) * num_drm_devices,
-                          8,
-                          VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
-   if (!drm_devices) {
-      result = vk_error(instance, VK_ERROR_OUT_OF_HOST_MEMORY);
-      goto out;
-   }
+   VkPhysicalDeviceVulkan13Properties core_1_3 = {
+      .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_PROPERTIES,
+   };
 
-   result = pvr_get_drm_devices(instance, drm_devices, num_drm_devices, NULL);
-   if (result != VK_SUCCESS)
-      goto out_free_drm_device_ptrs;
+   pvr_GetPhysicalDeviceProperties(physicalDevice, &pProperties->properties);
+   pvr_get_physical_device_properties_1_1(pdevice, &core_1_1);
+   pvr_get_physical_device_properties_1_2(pdevice, &core_1_2);
+   pvr_get_physical_device_properties_1_3(pdevice, &core_1_3);
 
-   /* First search for our render node... */
-   for (int i = 0; i < num_drm_devices; i++) {
-      drmDevice *const drm_dev = drm_devices[i];
+   vk_foreach_struct (ext, pProperties->pNext) {
+      if (vk_get_physical_device_core_1_1_property_ext(ext, &core_1_1))
+         continue;
 
-      if (drm_dev->bustype != DRM_BUS_PLATFORM)
+      if (vk_get_physical_device_core_1_2_property_ext(ext, &core_1_2))
          continue;
 
-      if (!(drm_dev->available_nodes & BITFIELD_BIT(DRM_NODE_RENDER)))
+      if (vk_get_physical_device_core_1_3_property_ext(ext, &core_1_3))
          continue;
 
-      config = pvr_drm_device_get_config(drm_dev);
-      if (config) {
-         drm_render_device = drm_dev;
+      switch (ext->sType) {
+      default: {
+         pvr_debug_ignored_stype(ext->sType);
          break;
       }
+      }
    }
+}
 
-   if (!config) {
-      result = VK_SUCCESS;
-      goto out_free_drm_devices;
-   }
-
-   mesa_logd("Found compatible render device '%s'.",
-             drm_render_device->nodes[DRM_NODE_RENDER]);
+VkResult pvr_EnumerateInstanceVersion(uint32_t *pApiVersion)
+{
+   *pApiVersion = PVR_API_VERSION;
+   return VK_SUCCESS;
+}
 
-   /* ...then find the compatible display node. */
-   for (int i = 0; i < num_drm_devices; i++) {
-      drmDevice *const drm_dev = drm_devices[i];
+VkResult
+pvr_EnumerateInstanceExtensionProperties(const char *pLayerName,
+                                         uint32_t *pPropertyCount,
+                                         VkExtensionProperties *pProperties)
+{
+   if (pLayerName)
+      return vk_error(NULL, VK_ERROR_LAYER_NOT_PRESENT);
 
-      if (!(drm_dev->available_nodes & BITFIELD_BIT(DRM_NODE_PRIMARY)))
-         continue;
+   return vk_enumerate_instance_extension_properties(&pvr_instance_extensions,
+                                                     pPropertyCount,
+                                                     pProperties);
+}
 
-      if (pvr_drm_device_compatible(&config->display, drm_dev)) {
-         drm_display_device = drm_dev;
-         break;
-      }
-   }
+static 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);
 
-   if (!drm_display_device) {
-      mesa_loge("Render device '%s' has no compatible display device.",
-                drm_render_device->nodes[DRM_NODE_RENDER]);
-      result = VK_SUCCESS;
-      goto out_free_drm_devices;
-   }
+   /* 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.
+    */
 
-   mesa_logd("Found compatible display device '%s'.",
-             drm_display_device->nodes[DRM_NODE_PRIMARY]);
+   if (pdevice->compiler)
+      ralloc_free(pdevice->compiler);
 
-   pdevice = vk_alloc(&vk_instance->alloc,
-                      sizeof(*pdevice),
-                      8,
-                      VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
-   if (!pdevice) {
-      result = vk_error(instance, VK_ERROR_OUT_OF_HOST_MEMORY);
-      goto out_free_drm_devices;
-   }
+   pvr_wsi_finish(pdevice);
 
-   result = pvr_physical_device_init(pdevice,
-                                     instance,
-                                     drm_render_device,
-                                     drm_display_device);
-   if (result != VK_SUCCESS) {
-      if (result == VK_ERROR_INCOMPATIBLE_DRIVER)
-         result = VK_SUCCESS;
+   free(pdevice->name);
 
-      goto err_free_pdevice;
-   }
+   if (pdevice->ws)
+      pvr_winsys_destroy(pdevice->ws);
 
-   if (PVR_IS_DEBUG_SET(INFO)) {
-      pvr_physical_device_dump_info(
-         pdevice,
-         drm_display_device->deviceinfo.platform->compatible,
-         drm_render_device->deviceinfo.platform->compatible);
-   }
+   vk_free(&pdevice->vk.instance->alloc, pdevice->render_path);
+   vk_free(&pdevice->vk.instance->alloc, pdevice->display_path);
 
-   list_add(&pdevice->vk.link, &vk_instance->physical_devices.list);
+   vk_physical_device_finish(&pdevice->vk);
 
-   result = VK_SUCCESS;
-   goto out_free_drm_devices;
+   vk_free(&pdevice->vk.instance->alloc, pdevice);
+}
 
-err_free_pdevice:
-   vk_free(&vk_instance->alloc, pdevice);
+void pvr_DestroyInstance(VkInstance _instance,
+                         const VkAllocationCallbacks *pAllocator)
+{
+   PVR_FROM_HANDLE(pvr_instance, instance, _instance);
 
-out_free_drm_devices:
-   drmFreeDevices(drm_devices, num_drm_devices);
+   if (!instance)
+      return;
 
-out_free_drm_device_ptrs:
-   vk_free(&vk_instance->alloc, drm_devices);
+   VG(VALGRIND_DESTROY_MEMPOOL(instance));
 
-out:
-   return result;
+   vk_instance_finish(&instance->vk);
+   vk_free(&instance->vk.alloc, instance);
 }
 
-VkResult pvr_CreateInstance(const VkInstanceCreateInfo *pCreateInfo,
-                            const VkAllocationCallbacks *pAllocator,
-                            VkInstance *pInstance)
+static uint64_t pvr_compute_heap_size(void)
 {
-   struct vk_instance_dispatch_table dispatch_table;
-   struct pvr_instance *instance;
-   VkResult result;
-
-   assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO);
+   /* Query the total ram from the system */
+   uint64_t total_ram;
+   if (!os_get_total_physical_memory(&total_ram))
+      return 0;
 
-   if (!pAllocator)
-      pAllocator = vk_default_allocator();
+   /* 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;
 
-   instance = vk_alloc(pAllocator,
-                       sizeof(*instance),
-                       8,
-                       VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
-   if (!instance)
-      return vk_error(NULL, VK_ERROR_OUT_OF_HOST_MEMORY);
+   return available_ram;
+}
 
-   vk_instance_dispatch_table_from_entrypoints(&dispatch_table,
-                                               &pvr_instance_entrypoints,
-                                               true);
+static 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_features supported_features;
+   struct pvr_winsys *ws;
+   char *display_path;
+   char *render_path;
+   VkResult result;
 
-   vk_instance_dispatch_table_from_entrypoints(&dispatch_table,
-                                               &wsi_instance_entrypoints,
-                                               false);
+   if (!getenv("PVR_I_WANT_A_BROKEN_VULKAN_DRIVER")) {
+      return vk_errorf(instance,
+                       VK_ERROR_INCOMPATIBLE_DRIVER,
+                       "WARNING: powervr is not a conformant Vulkan "
+                       "implementation. Pass "
+                       "PVR_I_WANT_A_BROKEN_VULKAN_DRIVER=1 if you know "
+                       "what you're doing.");
+   }
 
-   result = vk_instance_init(&instance->vk,
-                             &pvr_instance_extensions,
-                             &dispatch_table,
-                             pCreateInfo,
-                             pAllocator);
-   if (result != VK_SUCCESS) {
-      vk_free(pAllocator, instance);
-      return 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;
    }
 
-   pvr_process_debug_variable();
+   if (instance->vk.enabled_extensions.KHR_display) {
+      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;
+   }
 
-   instance->active_device_count = 0;
+   result =
+      pvr_winsys_create(render_path, display_path, &instance->vk.alloc, &ws);
+   if (result != VK_SUCCESS)
+      goto err_vk_free_display_path;
 
-   instance->vk.physical_devices.enumerate = pvr_physical_device_enumerate;
-   instance->vk.physical_devices.destroy = pvr_physical_device_destroy;
+   pdevice->instance = instance;
+   pdevice->render_path = render_path;
+   pdevice->display_path = display_path;
+   pdevice->ws = ws;
 
-   VG(VALGRIND_CREATE_MEMPOOL(instance, 0, false));
+   result = ws->ops->device_info_init(ws,
+                                      &pdevice->dev_info,
+                                      &pdevice->dev_runtime_info);
+   if (result != VK_SUCCESS)
+      goto err_pvr_winsys_destroy;
 
-   *pInstance = pvr_instance_to_handle(instance);
+   pvr_physical_device_get_supported_extensions(&supported_extensions);
+   pvr_physical_device_get_supported_features(&pdevice->dev_info,
+                                              &supported_features);
 
-   return VK_SUCCESS;
-}
+   vk_physical_device_dispatch_table_from_entrypoints(
+      &dispatch_table,
+      &pvr_physical_device_entrypoints,
+      true);
 
-static uint32_t pvr_get_simultaneous_num_allocs(
-   const struct pvr_device_info *dev_info,
-   ASSERTED const struct pvr_device_runtime_info *dev_runtime_info)
-{
-   uint32_t min_cluster_per_phantom;
+   vk_physical_device_dispatch_table_from_entrypoints(
+      &dispatch_table,
+      &wsi_physical_device_entrypoints,
+      false);
 
-   if (PVR_HAS_FEATURE(dev_info, s8xe))
-      return PVR_GET_FEATURE_VALUE(dev_info, num_raster_pipes, 0U);
+   result = vk_physical_device_init(&pdevice->vk,
+                                    &instance->vk,
+                                    &supported_extensions,
+                                    &supported_features,
+                                    NULL,
+                                    &dispatch_table);
+   if (result != VK_SUCCESS)
+      goto err_pvr_winsys_destroy;
 
-   assert(dev_runtime_info->num_phantoms == 1);
-   min_cluster_per_phantom = PVR_GET_FEATURE_VALUE(dev_info, num_clusters, 1U);
+   pdevice->vk.supported_sync_types = ws->sync_types;
 
-   if (min_cluster_per_phantom >= 4)
-      return 1;
-   else if (min_cluster_per_phantom == 2)
-      return 2;
-   else
-      return 4;
-}
+   result = pvr_physical_device_init_uuids(pdevice);
+   if (result != VK_SUCCESS)
+      goto err_vk_physical_device_finish;
 
-uint32_t pvr_calc_fscommon_size_and_tiles_in_flight(
-   const struct pvr_device_info *dev_info,
-   const struct pvr_device_runtime_info *dev_runtime_info,
-   uint32_t fs_common_size,
-   uint32_t min_tiles_in_flight)
-{
-   const uint32_t available_shareds =
-      dev_runtime_info->reserved_shared_size - dev_runtime_info->max_coeffs;
-   const uint32_t max_tiles_in_flight =
-      PVR_GET_FEATURE_VALUE(dev_info, isp_max_tiles_in_flight, 1U);
-   uint32_t num_tile_in_flight;
-   uint32_t num_allocs;
+   if (asprintf(&pdevice->name,
+                "Imagination PowerVR %s %s",
+                pdevice->dev_info.ident.series_name,
+                pdevice->dev_info.ident.public_name) < 0) {
+      result = vk_errorf(instance,
+                         VK_ERROR_OUT_OF_HOST_MEMORY,
+                         "Unable to allocate memory to store device name");
+      goto err_vk_physical_device_finish;
+   }
 
-   if (fs_common_size == 0)
-      return max_tiles_in_flight;
+   /* 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;
 
-   num_allocs = pvr_get_simultaneous_num_allocs(dev_info, dev_runtime_info);
+   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;
 
-   if (fs_common_size == UINT32_MAX) {
-      uint32_t max_common_size = available_shareds;
+   result = pvr_wsi_init(pdevice);
+   if (result != VK_SUCCESS) {
+      vk_error(instance, result);
+      goto err_free_name;
+   }
 
-      num_allocs *= MIN2(min_tiles_in_flight, max_tiles_in_flight);
+   pdevice->compiler = rogue_compiler_create(&pdevice->dev_info);
+   if (!pdevice->compiler) {
+      result = vk_errorf(instance,
+                         VK_ERROR_INITIALIZATION_FAILED,
+                         "Failed to initialize Rogue compiler");
+      goto err_wsi_finish;
+   }
 
-      if (!PVR_HAS_ERN(dev_info, 38748)) {
-         /* Hardware needs space for one extra shared allocation. */
-         num_allocs += 1;
-      }
+   return VK_SUCCESS;
 
-      /* Double resource requirements to deal with fragmentation. */
-      max_common_size /= num_allocs * 2;
-      max_common_size = MIN2(max_common_size, ROGUE_MAX_PIXEL_SHARED_REGISTERS);
-      max_common_size =
-         ROUND_DOWN_TO(max_common_size,
-                       PVRX(TA_STATE_PDS_SIZEINFO2_USC_SHAREDSIZE_UNIT_SIZE));
+err_wsi_finish:
+   pvr_wsi_finish(pdevice);
 
-      return max_common_size;
-   }
+err_free_name:
+   free(pdevice->name);
 
-   num_tile_in_flight = available_shareds / (fs_common_size * 2);
+err_vk_physical_device_finish:
+   vk_physical_device_finish(&pdevice->vk);
 
-   if (!PVR_HAS_ERN(dev_info, 38748))
-      num_tile_in_flight -= 1;
+err_pvr_winsys_destroy:
+   pvr_winsys_destroy(ws);
 
-   num_tile_in_flight /= num_allocs;
+err_vk_free_display_path:
+   vk_free(&instance->vk.alloc, display_path);
 
-#if defined(DEBUG)
-   /* Validate the above result. */
+err_vk_free_render_path:
+   vk_free(&instance->vk.alloc, render_path);
 
-   assert(num_tile_in_flight >= MIN2(num_tile_in_flight, max_tiles_in_flight));
-   num_allocs *= num_tile_in_flight;
+err_out:
+   return result;
+}
 
-   if (!PVR_HAS_ERN(dev_info, 38748)) {
-      /* Hardware needs space for one extra shared allocation. */
-      num_allocs += 1;
+static VkResult pvr_get_drm_devices(void *const obj,
+                                    drmDevicePtr *const devices,
+                                    const int max_devices,
+                                    int *const num_devices_out)
+{
+   int ret = drmGetDevices2(0, devices, max_devices);
+   if (ret < 0) {
+      return vk_errorf(obj,
+                       VK_ERROR_INITIALIZATION_FAILED,
+                       "Failed to enumerate drm devices (errno %d: %s)",
+                       -ret,
+                       strerror(-ret));
    }
 
-   assert(fs_common_size <= available_shareds / (num_allocs * 2));
-#endif
+   if (num_devices_out)
+      *num_devices_out = ret;
 
-   return MIN2(num_tile_in_flight, max_tiles_in_flight);
+   return VK_SUCCESS;
 }
 
-struct pvr_descriptor_limits {
-   uint32_t max_per_stage_resources;
-   uint32_t max_per_stage_samplers;
-   uint32_t max_per_stage_uniform_buffers;
-   uint32_t max_per_stage_storage_buffers;
-   uint32_t max_per_stage_sampled_images;
-   uint32_t max_per_stage_storage_images;
-   uint32_t max_per_stage_input_attachments;
-};
-
-static const struct pvr_descriptor_limits *
-pvr_get_physical_device_descriptor_limits(
-   const struct pvr_device_info *dev_info,
-   const struct pvr_device_runtime_info *dev_runtime_info)
+static bool
+pvr_drm_device_compatible(const struct pvr_drm_device_info *const info,
+                          drmDevice *const drm_dev)
 {
-   enum pvr_descriptor_cs_level {
-      /* clang-format off */
-      CS4096, /* 6XT and some XE cores with large CS. */
-      CS2560, /* Mid range Rogue XE cores. */
-      CS2048, /* Low end Rogue XE cores. */
-      CS1536, /* Ultra-low-end 9XEP. */
-      CS680,  /* lower limits for older devices. */
-      CS408,  /* 7XE. */
-      /* clang-format on */
-   };
-
-   static const struct pvr_descriptor_limits descriptor_limits[] = {
-      [CS4096] = { 1160U, 256U, 192U, 144U, 256U, 256U, 8U, },
-      [CS2560] = {  648U, 128U, 128U, 128U, 128U, 128U, 8U, },
-      [CS2048] = {  584U, 128U,  96U,  64U, 128U, 128U, 8U, },
-      [CS1536] = {  456U,  64U,  96U,  64U, 128U,  64U, 8U, },
-      [CS680]  = {  224U,  32U,  64U,  36U,  48U,   8U, 8U, },
-      [CS408]  = {  128U,  16U,  40U,  28U,  16U,   8U, 8U, },
-   };
-
-   const uint32_t common_size =
-      pvr_calc_fscommon_size_and_tiles_in_flight(dev_info,
-                                                 dev_runtime_info,
-                                                 UINT32_MAX,
-                                                 1);
-   enum pvr_descriptor_cs_level cs_level;
+   char **const compatible = drm_dev->deviceinfo.platform->compatible;
 
-   if (common_size >= 2048) {
-      cs_level = CS2048;
-   } else if (common_size >= 1526) {
-      cs_level = CS1536;
-   } else if (common_size >= 680) {
-      cs_level = CS680;
-   } else if (common_size >= 408) {
-      cs_level = CS408;
-   } else {
-      mesa_loge("This core appears to have a very limited amount of shared "
-                "register space and may not meet the Vulkan spec limits.");
-      abort();
+   for (char **compat = compatible; *compat; compat++) {
+      if (strncmp(*compat, info->name, info->len) == 0)
+         return true;
    }
 
-   return &descriptor_limits[cs_level];
+   return false;
 }
 
-static void
-pvr_get_physical_device_properties_1_1(struct pvr_physical_device *pdevice,
-                                       VkPhysicalDeviceVulkan11Properties *p)
+static const struct pvr_drm_device_config *
+pvr_drm_device_get_config(drmDevice *const drm_dev)
 {
-   assert(p->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES);
+   for (size_t i = 0U; i < ARRAY_SIZE(pvr_drm_configs); i++) {
+      if (pvr_drm_device_compatible(&pvr_drm_configs[i].render, drm_dev))
+         return &pvr_drm_configs[i];
+   }
+
+   return NULL;
 }
 
 static void
-pvr_get_physical_device_properties_1_2(struct pvr_physical_device *pdevice,
-                                       VkPhysicalDeviceVulkan12Properties *p)
+pvr_physical_device_dump_info(const struct pvr_physical_device *pdevice,
+                              char *const *comp_display,
+                              char *const *comp_render)
 {
-   assert(p->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_PROPERTIES);
+   drmVersionPtr version_display, version_render;
+   struct pvr_device_dump_info info;
 
-   /* VK_KHR_driver_properties */
-   p->driverID = VK_DRIVER_ID_IMAGINATION_OPEN_SOURCE_MESA;
-   memset(p->driverName, 0, sizeof(p->driverName));
-   snprintf(p->driverName,
-            VK_MAX_DRIVER_NAME_SIZE,
-            "Imagination open-source Mesa driver");
-   memset(p->driverInfo, 0, sizeof(p->driverInfo));
-   snprintf(p->driverInfo,
-            VK_MAX_DRIVER_INFO_SIZE,
-            ("Mesa " PACKAGE_VERSION MESA_GIT_SHA1));
-   p->conformanceVersion = (VkConformanceVersion){
-      .major = 1,
-      .minor = 3,
-      .subminor = 4,
-      .patch = 1,
-   };
+   version_display = drmGetVersion(pdevice->ws->display_fd);
+   if (!version_display)
+      return;
 
-   /* VK_KHR_timeline_semaphore */
-   p->maxTimelineSemaphoreValueDifference = UINT64_MAX;
-}
+   version_render = drmGetVersion(pdevice->ws->render_fd);
+   if (!version_render) {
+      drmFreeVersion(version_display);
+      return;
+   }
 
-static void
-pvr_get_physical_device_properties_1_3(struct pvr_physical_device *pdevice,
-                                       VkPhysicalDeviceVulkan13Properties *p)
-{
-   assert(p->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_PROPERTIES);
+   info.device_info = &pdevice->dev_info;
+   info.device_runtime_info = &pdevice->dev_runtime_info;
+   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_GetPhysicalDeviceProperties(VkPhysicalDevice physicalDevice,
-                                     VkPhysicalDeviceProperties *pProperties)
+static VkResult
+pvr_physical_device_enumerate(struct vk_instance *const vk_instance)
 {
-   PVR_FROM_HANDLE(pvr_physical_device, pdevice, physicalDevice);
-   const struct pvr_device_info *const dev_info = &pdevice->dev_info;
+   struct pvr_instance *const instance =
+      container_of(vk_instance, struct pvr_instance, vk);
 
-   const struct pvr_descriptor_limits *descriptor_limits =
-      pvr_get_physical_device_descriptor_limits(dev_info,
-                                                &pdevice->dev_runtime_info);
+   const struct pvr_drm_device_config *config = NULL;
 
-   /* 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);
+   drmDevicePtr drm_display_device = NULL;
+   drmDevicePtr drm_render_device = NULL;
+   struct pvr_physical_device *pdevice;
+   drmDevicePtr *drm_devices;
+   int num_drm_devices = 0;
+   VkResult result;
 
-   /* Default value based on the minimum value found in all existing cores. */
-   const uint32_t uvs_banks = PVR_GET_FEATURE_VALUE(dev_info, uvs_banks, 2);
+   result = pvr_get_drm_devices(instance, NULL, 0, &num_drm_devices);
+   if (result != VK_SUCCESS)
+      goto out;
 
-   /* Default value based on the minimum value found in all existing cores. */
-   const uint32_t uvs_pba_entries =
-      PVR_GET_FEATURE_VALUE(dev_info, uvs_pba_entries, 160);
+   if (num_drm_devices == 0) {
+      result = VK_SUCCESS;
+      goto out;
+   }
 
-   /* Default value based on the minimum value found in all existing cores. */
-   const uint32_t num_user_clip_planes =
-      PVR_GET_FEATURE_VALUE(dev_info, num_user_clip_planes, 8);
+   drm_devices = vk_alloc(&vk_instance->alloc,
+                          sizeof(*drm_devices) * num_drm_devices,
+                          8,
+                          VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
+   if (!drm_devices) {
+      result = vk_error(instance, VK_ERROR_OUT_OF_HOST_MEMORY);
+      goto out;
+   }
 
-   const uint32_t sub_pixel_precision =
-      PVR_HAS_FEATURE(dev_info, simple_internal_parameter_format) ? 4U : 8U;
+   result = pvr_get_drm_devices(instance, drm_devices, num_drm_devices, NULL);
+   if (result != VK_SUCCESS)
+      goto out_free_drm_device_ptrs;
 
-   const uint32_t max_render_size = rogue_get_render_size_max(dev_info);
+   /* First search for our render node... */
+   for (int i = 0; i < num_drm_devices; i++) {
+      drmDevice *const drm_dev = drm_devices[i];
 
-   const uint32_t max_sample_bits = ((max_multisample << 1) - 1);
+      if (drm_dev->bustype != DRM_BUS_PLATFORM)
+         continue;
 
-   const uint32_t max_user_vertex_components =
-      ((uvs_banks <= 8U) && (uvs_pba_entries == 160U)) ? 64U : 128U;
+      if (!(drm_dev->available_nodes & BITFIELD_BIT(DRM_NODE_RENDER)))
+         continue;
 
-   /* 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
-    */
+      config = pvr_drm_device_get_config(drm_dev);
+      if (config) {
+         drm_render_device = drm_dev;
+         break;
+      }
+   }
 
-   /* 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);
+   if (!config) {
+      result = VK_SUCCESS;
+      goto out_free_drm_devices;
+   }
 
-   /* 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);
+   mesa_logd("Found compatible render device '%s'.",
+             drm_render_device->nodes[DRM_NODE_RENDER]);
 
-   const uint32_t max_compute_work_group_invocations =
-      (usc_slots * max_instances_per_pds_task >= 512U) ? 512U : 384U;
+   /* ...then find the compatible display node. */
+   for (int i = 0; i < num_drm_devices; i++) {
+      drmDevice *const drm_dev = drm_devices[i];
 
-   VkPhysicalDeviceLimits limits = {
-      .maxImageDimension1D = max_render_size,
-      .maxImageDimension2D = max_render_size,
-      .maxImageDimension3D = PVR_MAX_TEXTURE_EXTENT_Z,
-      .maxImageDimensionCube = max_render_size,
-      .maxImageArrayLayers = PVR_MAX_ARRAY_LAYERS,
-      .maxTexelBufferElements = 64U * 1024U,
-      .maxUniformBufferRange = 128U * 1024U * 1024U,
-      .maxStorageBufferRange = 128U * 1024U * 1024U,
-      .maxPushConstantsSize = PVR_MAX_PUSH_CONSTANTS_SIZE,
-      .maxMemoryAllocationCount = UINT32_MAX,
-      .maxSamplerAllocationCount = UINT32_MAX,
-      .bufferImageGranularity = 1U,
-      .sparseAddressSpaceSize = 256ULL * 1024ULL * 1024ULL * 1024ULL,
+      if (!(drm_dev->available_nodes & BITFIELD_BIT(DRM_NODE_PRIMARY)))
+         continue;
 
-      /* Maximum number of descriptor sets that can be bound at the same time.
-       */
-      .maxBoundDescriptorSets = PVR_MAX_DESCRIPTOR_SETS,
+      if (pvr_drm_device_compatible(&config->display, drm_dev)) {
+         drm_display_device = drm_dev;
+         break;
+      }
+   }
 
-      .maxPerStageResources = descriptor_limits->max_per_stage_resources,
-      .maxPerStageDescriptorSamplers =
-         descriptor_limits->max_per_stage_samplers,
-      .maxPerStageDescriptorUniformBuffers =
-         descriptor_limits->max_per_stage_uniform_buffers,
-      .maxPerStageDescriptorStorageBuffers =
-         descriptor_limits->max_per_stage_storage_buffers,
-      .maxPerStageDescriptorSampledImages =
-         descriptor_limits->max_per_stage_sampled_images,
-      .maxPerStageDescriptorStorageImages =
-         descriptor_limits->max_per_stage_storage_images,
-      .maxPerStageDescriptorInputAttachments =
-         descriptor_limits->max_per_stage_input_attachments,
+   if (!drm_display_device) {
+      mesa_loge("Render device '%s' has no compatible display device.",
+                drm_render_device->nodes[DRM_NODE_RENDER]);
+      result = VK_SUCCESS;
+      goto out_free_drm_devices;
+   }
 
-      .maxDescriptorSetSamplers = 256U,
-      .maxDescriptorSetUniformBuffers = 256U,
-      .maxDescriptorSetUniformBuffersDynamic =
-         PVR_MAX_DESCRIPTOR_SET_UNIFORM_DYNAMIC_BUFFERS,
-      .maxDescriptorSetStorageBuffers = 256U,
-      .maxDescriptorSetStorageBuffersDynamic =
-         PVR_MAX_DESCRIPTOR_SET_STORAGE_DYNAMIC_BUFFERS,
-      .maxDescriptorSetSampledImages = 256U,
-      .maxDescriptorSetStorageImages = 256U,
-      .maxDescriptorSetInputAttachments = 256U,
+   mesa_logd("Found compatible display device '%s'.",
+             drm_display_device->nodes[DRM_NODE_PRIMARY]);
+
+   pdevice = vk_alloc(&vk_instance->alloc,
+                      sizeof(*pdevice),
+                      8,
+                      VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
+   if (!pdevice) {
+      result = vk_error(instance, VK_ERROR_OUT_OF_HOST_MEMORY);
+      goto out_free_drm_devices;
+   }
+
+   result = pvr_physical_device_init(pdevice,
+                                     instance,
+                                     drm_render_device,
+                                     drm_display_device);
+   if (result != VK_SUCCESS) {
+      if (result == VK_ERROR_INCOMPATIBLE_DRIVER)
+         result = VK_SUCCESS;
+
+      goto err_free_pdevice;
+   }
+
+   if (PVR_IS_DEBUG_SET(INFO)) {
+      pvr_physical_device_dump_info(
+         pdevice,
+         drm_display_device->deviceinfo.platform->compatible,
+         drm_render_device->deviceinfo.platform->compatible);
+   }
+
+   list_add(&pdevice->vk.link, &vk_instance->physical_devices.list);
+
+   result = VK_SUCCESS;
+   goto out_free_drm_devices;
 
-      /* Vertex Shader Limits */
-      .maxVertexInputAttributes = PVR_MAX_VERTEX_INPUT_BINDINGS,
-      .maxVertexInputBindings = PVR_MAX_VERTEX_INPUT_BINDINGS,
-      .maxVertexInputAttributeOffset = 0xFFFF,
-      .maxVertexInputBindingStride = 1024U * 1024U * 1024U * 2U,
-      .maxVertexOutputComponents = max_user_vertex_components,
+err_free_pdevice:
+   vk_free(&vk_instance->alloc, pdevice);
 
-      /* Tessellation Limits */
-      .maxTessellationGenerationLevel = 0,
-      .maxTessellationPatchSize = 0,
-      .maxTessellationControlPerVertexInputComponents = 0,
-      .maxTessellationControlPerVertexOutputComponents = 0,
-      .maxTessellationControlPerPatchOutputComponents = 0,
-      .maxTessellationControlTotalOutputComponents = 0,
-      .maxTessellationEvaluationInputComponents = 0,
-      .maxTessellationEvaluationOutputComponents = 0,
+out_free_drm_devices:
+   drmFreeDevices(drm_devices, num_drm_devices);
 
-      /* Geometry Shader Limits */
-      .maxGeometryShaderInvocations = 0,
-      .maxGeometryInputComponents = 0,
-      .maxGeometryOutputComponents = 0,
-      .maxGeometryOutputVertices = 0,
-      .maxGeometryTotalOutputComponents = 0,
+out_free_drm_device_ptrs:
+   vk_free(&vk_instance->alloc, drm_devices);
 
-      /* Fragment Shader Limits */
-      .maxFragmentInputComponents = max_user_vertex_components,
-      .maxFragmentOutputAttachments = PVR_MAX_COLOR_ATTACHMENTS,
-      .maxFragmentDualSrcAttachments = 0,
-      .maxFragmentCombinedOutputResources =
-         descriptor_limits->max_per_stage_storage_buffers +
-         descriptor_limits->max_per_stage_storage_images +
-         PVR_MAX_COLOR_ATTACHMENTS,
+out:
+   return result;
+}
 
-      /* Compute Shader Limits */
-      .maxComputeSharedMemorySize = 16U * 1024U,
-      .maxComputeWorkGroupCount = { 64U * 1024U, 64U * 1024U, 64U * 1024U },
-      .maxComputeWorkGroupInvocations = max_compute_work_group_invocations,
-      .maxComputeWorkGroupSize = { max_compute_work_group_invocations,
-                                   max_compute_work_group_invocations,
-                                   64U },
+VkResult pvr_CreateInstance(const VkInstanceCreateInfo *pCreateInfo,
+                            const VkAllocationCallbacks *pAllocator,
+                            VkInstance *pInstance)
+{
+   struct vk_instance_dispatch_table dispatch_table;
+   struct pvr_instance *instance;
+   VkResult result;
 
-      /* Rasterization Limits */
-      .subPixelPrecisionBits = sub_pixel_precision,
-      .subTexelPrecisionBits = 8U,
-      .mipmapPrecisionBits = 8U,
+   assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO);
 
-      .maxDrawIndexedIndexValue = UINT32_MAX,
-      .maxDrawIndirectCount = 2U * 1024U * 1024U * 1024U,
-      .maxSamplerLodBias = 16.0f,
-      .maxSamplerAnisotropy = 1.0f,
-      .maxViewports = PVR_MAX_VIEWPORTS,
+   if (!pAllocator)
+      pAllocator = vk_default_allocator();
 
-      .maxViewportDimensions[0] = max_render_size,
-      .maxViewportDimensions[1] = max_render_size,
-      .viewportBoundsRange[0] = -(int32_t)(2U * max_render_size),
-      .viewportBoundsRange[1] = 2U * max_render_size,
+   instance = vk_alloc(pAllocator,
+                       sizeof(*instance),
+                       8,
+                       VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
+   if (!instance)
+      return vk_error(NULL, VK_ERROR_OUT_OF_HOST_MEMORY);
 
-      .viewportSubPixelBits = 0,
-      .minMemoryMapAlignment = 64U,
-      .minTexelBufferOffsetAlignment = 16U,
-      .minUniformBufferOffsetAlignment = 4U,
-      .minStorageBufferOffsetAlignment = 4U,
+   vk_instance_dispatch_table_from_entrypoints(&dispatch_table,
+                                               &pvr_instance_entrypoints,
+                                               true);
 
-      .minTexelOffset = -8,
-      .maxTexelOffset = 7U,
-      .minTexelGatherOffset = -8,
-      .maxTexelGatherOffset = 7,
-      .minInterpolationOffset = -0.5,
-      .maxInterpolationOffset = 0.5,
-      .subPixelInterpolationOffsetBits = 4U,
+   vk_instance_dispatch_table_from_entrypoints(&dispatch_table,
+                                               &wsi_instance_entrypoints,
+                                               false);
 
-      .maxFramebufferWidth = max_render_size,
-      .maxFramebufferHeight = max_render_size,
-      .maxFramebufferLayers = PVR_MAX_FRAMEBUFFER_LAYERS,
+   result = vk_instance_init(&instance->vk,
+                             &pvr_instance_extensions,
+                             &dispatch_table,
+                             pCreateInfo,
+                             pAllocator);
+   if (result != VK_SUCCESS) {
+      vk_free(pAllocator, instance);
+      return result;
+   }
 
-      .framebufferColorSampleCounts = max_sample_bits,
-      .framebufferDepthSampleCounts = max_sample_bits,
-      .framebufferStencilSampleCounts = max_sample_bits,
-      .framebufferNoAttachmentsSampleCounts = max_sample_bits,
-      .maxColorAttachments = PVR_MAX_COLOR_ATTACHMENTS,
-      .sampledImageColorSampleCounts = max_sample_bits,
-      .sampledImageIntegerSampleCounts = max_sample_bits,
-      .sampledImageDepthSampleCounts = max_sample_bits,
-      .sampledImageStencilSampleCounts = max_sample_bits,
-      .storageImageSampleCounts = max_sample_bits,
-      .maxSampleMaskWords = 1U,
-      .timestampComputeAndGraphics = false,
-      .timestampPeriod = 0.0f,
-      .maxClipDistances = num_user_clip_planes,
-      .maxCullDistances = num_user_clip_planes,
-      .maxCombinedClipAndCullDistances = num_user_clip_planes,
-      .discreteQueuePriorities = 2U,
-      .pointSizeRange[0] = 1.0f,
-      .pointSizeRange[1] = 511.0f,
-      .pointSizeGranularity = 0.0625f,
-      .lineWidthRange[0] = 1.0f / 16.0f,
-      .lineWidthRange[1] = 16.0f,
-      .lineWidthGranularity = 1.0f / 16.0f,
-      .strictLines = false,
-      .standardSampleLocations = true,
-      .optimalBufferCopyOffsetAlignment = 4U,
-      .optimalBufferCopyRowPitchAlignment = 4U,
-      .nonCoherentAtomSize = 1U,
-   };
+   pvr_process_debug_variable();
 
-   *pProperties = (VkPhysicalDeviceProperties){
-      .apiVersion = PVR_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,
-      .limits = limits,
-      .sparseProperties = { 0 },
-   };
+   instance->active_device_count = 0;
 
-   snprintf(pProperties->deviceName,
-            sizeof(pProperties->deviceName),
-            "%s",
-            pdevice->name);
+   instance->vk.physical_devices.enumerate = pvr_physical_device_enumerate;
+   instance->vk.physical_devices.destroy = pvr_physical_device_destroy;
 
-   memcpy(pProperties->pipelineCacheUUID,
-          pdevice->pipeline_cache_uuid,
-          VK_UUID_SIZE);
+   VG(VALGRIND_CREATE_MEMPOOL(instance, 0, false));
+
+   *pInstance = pvr_instance_to_handle(instance);
+
+   return VK_SUCCESS;
 }
 
-void pvr_GetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevice,
-                                      VkPhysicalDeviceProperties2 *pProperties)
+static uint32_t pvr_get_simultaneous_num_allocs(
+   const struct pvr_device_info *dev_info,
+   ASSERTED const struct pvr_device_runtime_info *dev_runtime_info)
 {
-   PVR_FROM_HANDLE(pvr_physical_device, pdevice, physicalDevice);
+   uint32_t min_cluster_per_phantom;
 
-   VkPhysicalDeviceVulkan11Properties core_1_1 = {
-      .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES,
-   };
+   if (PVR_HAS_FEATURE(dev_info, s8xe))
+      return PVR_GET_FEATURE_VALUE(dev_info, num_raster_pipes, 0U);
 
-   VkPhysicalDeviceVulkan12Properties core_1_2 = {
-      .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_PROPERTIES,
-   };
+   assert(dev_runtime_info->num_phantoms == 1);
+   min_cluster_per_phantom = PVR_GET_FEATURE_VALUE(dev_info, num_clusters, 1U);
 
-   VkPhysicalDeviceVulkan13Properties core_1_3 = {
-      .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_PROPERTIES,
-   };
+   if (min_cluster_per_phantom >= 4)
+      return 1;
+   else if (min_cluster_per_phantom == 2)
+      return 2;
+   else
+      return 4;
+}
 
-   pvr_GetPhysicalDeviceProperties(physicalDevice, &pProperties->properties);
-   pvr_get_physical_device_properties_1_1(pdevice, &core_1_1);
-   pvr_get_physical_device_properties_1_2(pdevice, &core_1_2);
-   pvr_get_physical_device_properties_1_3(pdevice, &core_1_3);
+uint32_t pvr_calc_fscommon_size_and_tiles_in_flight(
+   const struct pvr_device_info *dev_info,
+   const struct pvr_device_runtime_info *dev_runtime_info,
+   uint32_t fs_common_size,
+   uint32_t min_tiles_in_flight)
+{
+   const uint32_t available_shareds =
+      dev_runtime_info->reserved_shared_size - dev_runtime_info->max_coeffs;
+   const uint32_t max_tiles_in_flight =
+      PVR_GET_FEATURE_VALUE(dev_info, isp_max_tiles_in_flight, 1U);
+   uint32_t num_tile_in_flight;
+   uint32_t num_allocs;
 
-   vk_foreach_struct (ext, pProperties->pNext) {
-      if (vk_get_physical_device_core_1_1_property_ext(ext, &core_1_1))
-         continue;
+   if (fs_common_size == 0)
+      return max_tiles_in_flight;
 
-      if (vk_get_physical_device_core_1_2_property_ext(ext, &core_1_2))
-         continue;
+   num_allocs = pvr_get_simultaneous_num_allocs(dev_info, dev_runtime_info);
 
-      if (vk_get_physical_device_core_1_3_property_ext(ext, &core_1_3))
-         continue;
+   if (fs_common_size == UINT32_MAX) {
+      uint32_t max_common_size = available_shareds;
 
-      switch (ext->sType) {
-      default: {
-         pvr_debug_ignored_stype(ext->sType);
-         break;
-      }
+      num_allocs *= MIN2(min_tiles_in_flight, max_tiles_in_flight);
+
+      if (!PVR_HAS_ERN(dev_info, 38748)) {
+         /* Hardware needs space for one extra shared allocation. */
+         num_allocs += 1;
       }
+
+      /* Double resource requirements to deal with fragmentation. */
+      max_common_size /= num_allocs * 2;
+      max_common_size = MIN2(max_common_size, ROGUE_MAX_PIXEL_SHARED_REGISTERS);
+      max_common_size =
+         ROUND_DOWN_TO(max_common_size,
+                       PVRX(TA_STATE_PDS_SIZEINFO2_USC_SHAREDSIZE_UNIT_SIZE));
+
+      return max_common_size;
+   }
+
+   num_tile_in_flight = available_shareds / (fs_common_size * 2);
+
+   if (!PVR_HAS_ERN(dev_info, 38748))
+      num_tile_in_flight -= 1;
+
+   num_tile_in_flight /= num_allocs;
+
+#if defined(DEBUG)
+   /* Validate the above result. */
+
+   assert(num_tile_in_flight >= MIN2(num_tile_in_flight, max_tiles_in_flight));
+   num_allocs *= num_tile_in_flight;
+
+   if (!PVR_HAS_ERN(dev_info, 38748)) {
+      /* Hardware needs space for one extra shared allocation. */
+      num_allocs += 1;
    }
+
+   assert(fs_common_size <= available_shareds / (num_allocs * 2));
+#endif
+
+   return MIN2(num_tile_in_flight, max_tiles_in_flight);
 }
 
 const static VkQueueFamilyProperties pvr_queue_family_properties = {