mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2026-06-13 00:48:20 +02:00
To use the common function, this gives up the warning about the memory being too small to meet the Vulkan spec for low end devices. Note: the common helper expose 25% for devices with <=1GiB but to adhere to the Vulkan spec, the value is clamped to 1GiB. Signed-off-by: Karmjit Mahil <karmjit.mahil@igalia.com> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/41242>
504 lines
14 KiB
C
504 lines
14 KiB
C
/*
|
|
* 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_instance.h"
|
|
|
|
#include <fcntl.h>
|
|
#include <xf86drm.h>
|
|
#include <xf86drmMode.h>
|
|
|
|
#include "vk_alloc.h"
|
|
#include "vk_log.h"
|
|
|
|
#include "wsi_common.h"
|
|
|
|
#include "util/build_id.h"
|
|
#include "util/driconf.h"
|
|
#include "util/os_misc.h"
|
|
|
|
#include "pvr_debug.h"
|
|
#include "pvr_device.h"
|
|
#include "pvr_entrypoints.h"
|
|
#include "pvr_macros.h"
|
|
#include "pvr_physical_device.h"
|
|
#include "pvr_winsys.h"
|
|
#include "pvr_wsi.h"
|
|
|
|
#if defined(VK_USE_PLATFORM_DISPLAY_KHR)
|
|
# define PVR_USE_WSI_PLATFORM_DISPLAY true
|
|
#else
|
|
# define PVR_USE_WSI_PLATFORM_DISPLAY false
|
|
#endif
|
|
|
|
static const struct vk_instance_extension_table pvr_instance_extensions = {
|
|
.KHR_device_group_creation = true,
|
|
.KHR_display = PVR_USE_WSI_PLATFORM_DISPLAY,
|
|
.KHR_external_fence_capabilities = true,
|
|
.KHR_external_memory_capabilities = true,
|
|
.KHR_external_semaphore_capabilities = true,
|
|
.KHR_get_display_properties2 = PVR_USE_WSI_PLATFORM_DISPLAY,
|
|
.KHR_get_physical_device_properties2 = true,
|
|
.KHR_get_surface_capabilities2 = PVR_USE_WSI_PLATFORM,
|
|
.KHR_surface = PVR_USE_WSI_PLATFORM,
|
|
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
|
|
.KHR_wayland_surface = true,
|
|
#endif
|
|
#ifdef VK_USE_PLATFORM_XCB_KHR
|
|
.KHR_xcb_surface = true,
|
|
#endif
|
|
#ifdef VK_USE_PLATFORM_XLIB_KHR
|
|
.KHR_xlib_surface = true,
|
|
#endif
|
|
.EXT_debug_report = true,
|
|
.EXT_debug_utils = true,
|
|
#ifndef VK_USE_PLATFORM_WIN32_KHR
|
|
.EXT_headless_surface = PVR_USE_WSI_PLATFORM,
|
|
#endif
|
|
};
|
|
|
|
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));
|
|
}
|
|
|
|
if (num_devices_out)
|
|
*num_devices_out = ret;
|
|
|
|
return VK_SUCCESS;
|
|
}
|
|
|
|
static bool
|
|
pvr_drm_device_is_compatible(drmDevicePtr drm_dev)
|
|
{
|
|
drmVersionPtr version;
|
|
bool is_pvr;
|
|
int32_t fd;
|
|
|
|
fd = open(drm_dev->nodes[DRM_NODE_RENDER], O_RDWR | O_CLOEXEC);
|
|
if (fd < 0) {
|
|
mesa_logd("Failed to open render node: %s\n",
|
|
drm_dev->nodes[DRM_NODE_RENDER]);
|
|
|
|
return false;
|
|
}
|
|
|
|
version = drmGetVersion(fd);
|
|
if (!version) {
|
|
mesa_logd("Failed to get version information for render node: %s\n",
|
|
drm_dev->nodes[DRM_NODE_RENDER]);
|
|
|
|
close(fd);
|
|
return false;
|
|
}
|
|
|
|
is_pvr = !strcmp(version->name, PVR_DRM_DRIVER_NAME);
|
|
|
|
#if defined(PVR_SUPPORT_SERVICES_DRIVER)
|
|
is_pvr |= !strcmp(version->name, PVR_SRV_DRIVER_NAME);
|
|
#endif /* defined(PVR_SUPPORT_SERVICES_DRIVER) */
|
|
|
|
drmFreeVersion(version);
|
|
close(fd);
|
|
|
|
return is_pvr;
|
|
}
|
|
|
|
static bool pvr_drm_device_is_compatible_display(drmDevicePtr drm_dev)
|
|
{
|
|
uint64_t has_dumb_buffer = 0;
|
|
uint64_t prime_caps = 0;
|
|
bool ret = false;
|
|
int32_t fd;
|
|
|
|
mesa_logd("Checking DRM primary node for compatibility: %s",
|
|
drm_dev->nodes[DRM_NODE_PRIMARY]);
|
|
fd = open(drm_dev->nodes[DRM_NODE_PRIMARY], O_RDWR | O_CLOEXEC);
|
|
if (fd < 0) {
|
|
mesa_logd("Failed to open display node: %s\n",
|
|
drm_dev->nodes[DRM_NODE_PRIMARY]);
|
|
return ret;
|
|
}
|
|
|
|
/* Must support KMS */
|
|
if (!drmIsKMS(fd)) {
|
|
mesa_logd("DRM device does not support KMS");
|
|
goto out;
|
|
}
|
|
|
|
/* Must support dumb buffers, as these are used by the PVR winsys to
|
|
* allocate device memory for PVR_WINSYS_BO_TYPE_DISPLAY buffer objects.
|
|
*/
|
|
if (drmGetCap(fd, DRM_CAP_DUMB_BUFFER, &has_dumb_buffer) ||
|
|
!has_dumb_buffer) {
|
|
mesa_logd("DRM device does not support dumb buffers");
|
|
goto out;
|
|
}
|
|
|
|
/* Must support PRIME export (so GPU can import dumb buffers) */
|
|
if (drmGetCap(fd, DRM_CAP_PRIME, &prime_caps)) {
|
|
mesa_loge("Failed to query DRM_CAP_PRIME: %s", strerror(errno));
|
|
goto out;
|
|
}
|
|
|
|
if (!(prime_caps & DRM_PRIME_CAP_EXPORT)) {
|
|
mesa_logd("DRM device lacks PRIME export support (caps: 0x%" PRIx64 ")",
|
|
prime_caps);
|
|
goto out;
|
|
}
|
|
|
|
ret = true;
|
|
|
|
out:
|
|
close(fd);
|
|
return ret;
|
|
}
|
|
|
|
static VkResult
|
|
pvr_physical_device_enumerate(struct vk_instance *const vk_instance)
|
|
{
|
|
struct pvr_instance *const instance =
|
|
container_of(vk_instance, struct pvr_instance, vk);
|
|
|
|
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;
|
|
|
|
result = pvr_get_drm_devices(instance, NULL, 0, &num_drm_devices);
|
|
if (result != VK_SUCCESS)
|
|
goto out;
|
|
|
|
if (num_drm_devices == 0) {
|
|
result = VK_SUCCESS;
|
|
goto out;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
result = pvr_get_drm_devices(instance, drm_devices, num_drm_devices, NULL);
|
|
if (result != VK_SUCCESS)
|
|
goto out_free_drm_device_ptrs;
|
|
|
|
/* First search for our render node... */
|
|
for (int i = 0; i < num_drm_devices; i++) {
|
|
drmDevice *const drm_dev = drm_devices[i];
|
|
|
|
if (drm_dev->bustype != DRM_BUS_PLATFORM)
|
|
continue;
|
|
|
|
if (!(drm_dev->available_nodes & BITFIELD_BIT(DRM_NODE_RENDER)))
|
|
continue;
|
|
|
|
|
|
if (!pvr_drm_device_is_compatible(drm_dev))
|
|
continue;
|
|
|
|
drm_render_device = drm_dev;
|
|
break;
|
|
}
|
|
|
|
if (!drm_render_device) {
|
|
result = VK_SUCCESS;
|
|
goto out_free_drm_devices;
|
|
}
|
|
|
|
mesa_logd("Found compatible render device '%s'.",
|
|
drm_render_device->nodes[DRM_NODE_RENDER]);
|
|
|
|
/* ...then find a compatible display node, if available. */
|
|
for (int i = 0; i < num_drm_devices; i++) {
|
|
drmDevice *const drm_dev = drm_devices[i];
|
|
|
|
if (drm_dev->bustype != DRM_BUS_PLATFORM)
|
|
continue;
|
|
|
|
if (!(drm_dev->available_nodes & BITFIELD_BIT(DRM_NODE_PRIMARY)))
|
|
continue;
|
|
|
|
if (!pvr_drm_device_is_compatible_display(drm_devices[i]))
|
|
continue;
|
|
|
|
drm_display_device = drm_dev;
|
|
mesa_logd("Found a compatible display device: '%s'.",
|
|
drm_display_device->nodes[DRM_NODE_PRIMARY]);
|
|
break;
|
|
}
|
|
|
|
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
|
|
? drm_display_device->deviceinfo.platform->compatible
|
|
: NULL,
|
|
drm_render_device->deviceinfo.platform->compatible);
|
|
}
|
|
|
|
list_add(&pdevice->vk.link, &vk_instance->physical_devices.list);
|
|
|
|
result = VK_SUCCESS;
|
|
goto out_free_drm_devices;
|
|
|
|
err_free_pdevice:
|
|
vk_free(&vk_instance->alloc, pdevice);
|
|
|
|
out_free_drm_devices:
|
|
drmFreeDevices(drm_devices, num_drm_devices);
|
|
|
|
out_free_drm_device_ptrs:
|
|
vk_free(&vk_instance->alloc, drm_devices);
|
|
|
|
out:
|
|
return result;
|
|
}
|
|
|
|
static bool
|
|
pvr_get_driver_build_sha(struct pvr_instance *instance)
|
|
{
|
|
const struct build_id_note *note;
|
|
unsigned build_id_len;
|
|
|
|
note = build_id_find_nhdr_for_addr(pvr_get_driver_build_sha);
|
|
if (!note) {
|
|
mesa_loge("Failed to find build-id.");
|
|
return false;
|
|
}
|
|
|
|
build_id_len = build_id_length(note);
|
|
if (build_id_len < BUILD_ID_EXPECTED_HASH_LENGTH) {
|
|
mesa_loge("Build-id too short. It needs to be a SHA.");
|
|
return false;
|
|
}
|
|
|
|
STATIC_ASSERT(sizeof(instance->driver_build_sha) == BLAKE3_KEY_LEN);
|
|
copy_build_id_to_sha1(instance->driver_build_sha, note);
|
|
|
|
return true;
|
|
}
|
|
|
|
/* clang-format off */
|
|
static const driOptionDescription pvr_dri_options[] = {
|
|
DRI_CONF_SECTION_PERFORMANCE
|
|
DRI_CONF_ADAPTIVE_SYNC(true)
|
|
DRI_CONF_VK_X11_OVERRIDE_MIN_IMAGE_COUNT(0)
|
|
DRI_CONF_VK_X11_STRICT_IMAGE_COUNT(false)
|
|
DRI_CONF_VK_X11_ENSURE_MIN_IMAGE_COUNT(false)
|
|
DRI_CONF_VK_XWAYLAND_WAIT_READY(false)
|
|
DRI_CONF_SECTION_END
|
|
|
|
DRI_CONF_SECTION_DEBUG
|
|
DRI_CONF_FORCE_VK_VENDOR()
|
|
DRI_CONF_VK_WSI_DISABLE_UNORDERED_SUBMITS(false)
|
|
DRI_CONF_VK_WSI_FORCE_BGRA8_UNORM_FIRST(false)
|
|
DRI_CONF_VK_WSI_FORCE_SWAPCHAIN_TO_CURRENT_EXTENT(false)
|
|
DRI_CONF_VK_X11_IGNORE_SUBOPTIMAL(false)
|
|
DRI_CONF_SECTION_END
|
|
|
|
DRI_CONF_SECTION_MISCELLANEOUS
|
|
DRI_CONF_HEAP_MEMORY_PERCENT(OS_GPU_HEAP_SIZE_HEURISTIC)
|
|
DRI_CONF_SECTION_END
|
|
};
|
|
/* clang-format on */
|
|
|
|
static void pvr_init_dri_options(struct pvr_instance *instance)
|
|
{
|
|
driParseOptionInfo(&instance->available_dri_options,
|
|
pvr_dri_options,
|
|
ARRAY_SIZE(pvr_dri_options));
|
|
driParseConfigFiles(&instance->dri_options, &instance->available_dri_options,
|
|
&(driConfigFileParseParams) {
|
|
.driverName = "pvr",
|
|
.applicationName = instance->vk.app_info.app_name,
|
|
.applicationVersion = instance->vk.app_info.app_version,
|
|
.engineName = instance->vk.app_info.engine_name,
|
|
.engineVersion = instance->vk.app_info.engine_version,
|
|
});
|
|
|
|
instance->force_vk_vendor =
|
|
driQueryOptioni(&instance->dri_options, "force_vk_vendor");
|
|
instance->heap_memory_percent =
|
|
driQueryOptionf(&instance->dri_options, "heap_memory_percent");
|
|
}
|
|
|
|
VkResult pvr_CreateInstance(const VkInstanceCreateInfo *pCreateInfo,
|
|
const VkAllocationCallbacks *pAllocator,
|
|
VkInstance *pInstance)
|
|
{
|
|
struct vk_instance_dispatch_table dispatch_table;
|
|
struct pvr_instance *instance;
|
|
VkResult result;
|
|
|
|
assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO);
|
|
|
|
if (!pAllocator)
|
|
pAllocator = vk_default_allocator();
|
|
|
|
instance = vk_alloc(pAllocator,
|
|
sizeof(*instance),
|
|
8,
|
|
VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
|
|
if (!instance)
|
|
return vk_error(NULL, VK_ERROR_OUT_OF_HOST_MEMORY);
|
|
|
|
vk_instance_dispatch_table_from_entrypoints(&dispatch_table,
|
|
&pvr_instance_entrypoints,
|
|
true);
|
|
|
|
vk_instance_dispatch_table_from_entrypoints(&dispatch_table,
|
|
&wsi_instance_entrypoints,
|
|
false);
|
|
|
|
result = vk_instance_init(&instance->vk,
|
|
&pvr_instance_extensions,
|
|
&dispatch_table,
|
|
pCreateInfo,
|
|
pAllocator);
|
|
if (result != VK_SUCCESS)
|
|
goto err_free_instance;
|
|
|
|
pvr_process_debug_variable();
|
|
pvr_init_dri_options(instance);
|
|
|
|
instance->active_device_count = 0;
|
|
|
|
instance->vk.physical_devices.enumerate = pvr_physical_device_enumerate;
|
|
instance->vk.physical_devices.destroy = pvr_physical_device_destroy;
|
|
|
|
VG(VALGRIND_CREATE_MEMPOOL(instance, 0, false));
|
|
|
|
if (!pvr_get_driver_build_sha(instance)) {
|
|
result = vk_errorf(NULL,
|
|
VK_ERROR_INITIALIZATION_FAILED,
|
|
"Failed to get driver build sha.");
|
|
goto err_free_instance;
|
|
}
|
|
|
|
*pInstance = pvr_instance_to_handle(instance);
|
|
|
|
return VK_SUCCESS;
|
|
|
|
err_free_instance:
|
|
driDestroyOptionCache(&instance->dri_options);
|
|
driDestroyOptionInfo(&instance->available_dri_options);
|
|
vk_free(pAllocator, instance);
|
|
return result;
|
|
}
|
|
|
|
void pvr_DestroyInstance(VkInstance _instance,
|
|
const VkAllocationCallbacks *pAllocator)
|
|
{
|
|
VK_FROM_HANDLE(pvr_instance, instance, _instance);
|
|
|
|
if (!instance)
|
|
return;
|
|
|
|
VG(VALGRIND_DESTROY_MEMPOOL(instance));
|
|
|
|
driDestroyOptionCache(&instance->dri_options);
|
|
driDestroyOptionInfo(&instance->available_dri_options);
|
|
|
|
vk_instance_finish(&instance->vk);
|
|
vk_free(&instance->vk.alloc, instance);
|
|
}
|
|
|
|
VkResult pvr_EnumerateInstanceVersion(uint32_t *pApiVersion)
|
|
{
|
|
*pApiVersion = VK_MAKE_API_VERSION(0, 1, 4, VK_HEADER_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);
|
|
}
|
|
|
|
PFN_vkVoidFunction pvr_GetInstanceProcAddr(VkInstance _instance,
|
|
const char *pName)
|
|
{
|
|
const struct vk_instance *vk_instance = NULL;
|
|
|
|
if (_instance != NULL) {
|
|
VK_FROM_HANDLE(pvr_instance, instance, _instance);
|
|
vk_instance = &instance->vk;
|
|
}
|
|
|
|
return vk_instance_get_proc_addr(vk_instance,
|
|
&pvr_instance_entrypoints,
|
|
pName);
|
|
}
|
|
|
|
/* With version 1+ of the loader interface the ICD should expose
|
|
* vk_icdGetInstanceProcAddr to work around certain LD_PRELOAD issues seen in
|
|
* apps.
|
|
*/
|
|
PUBLIC
|
|
VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL
|
|
vk_icdGetInstanceProcAddr(VkInstance instance, const char *pName)
|
|
{
|
|
return pvr_GetInstanceProcAddr(instance, pName);
|
|
}
|
|
|
|
VkResult pvr_EnumerateInstanceLayerProperties(uint32_t *pPropertyCount,
|
|
VkLayerProperties *pProperties)
|
|
{
|
|
if (!pProperties) {
|
|
*pPropertyCount = 0;
|
|
return VK_SUCCESS;
|
|
}
|
|
|
|
return vk_error(NULL, VK_ERROR_LAYER_NOT_PRESENT);
|
|
}
|