Skip to content
Snippets Groups Projects
Commit 0e40ae48 authored by Simon McVittie's avatar Simon McVittie
Browse files

graphics: Add EGL and Vulkan ICD enumeration


This is necessary for pressure-vessel containers to support Vulkan:
to be able to make the Vulkan ICDs available in the container, we have
to be able to find them, bind-mount them into a suitable location, and
provide corresponding ICD JSON metadata to the contained game.

Signed-off-by: default avatarSimon McVittie <smcv@collabora.com>
parent 0969843c
No related branches found
No related tags found
1 merge request!56graphics: Add EGL and Vulkan ICD enumeration
Showing
with 2029 additions and 2 deletions
......@@ -451,7 +451,9 @@ main (int argc,
gchar *inst_path = NULL;
gchar *rt_path = NULL;
int opt;
static const char * const multiarch_tuples[] = { SRT_ABI_I386, SRT_ABI_X86_64 };
static const char * const multiarch_tuples[] = { SRT_ABI_I386, SRT_ABI_X86_64, NULL };
GList *icds;
const GList *icd_iter;
while ((opt = getopt_long (argc, argv, "", long_options, NULL)) != -1)
{
......@@ -528,7 +530,9 @@ main (int argc,
json_builder_set_member_name (builder, "architectures");
json_builder_begin_object (builder);
for (gsize i = 0; i < G_N_ELEMENTS (multiarch_tuples); i++)
g_assert (multiarch_tuples[G_N_ELEMENTS (multiarch_tuples) - 1] == NULL);
for (gsize i = 0; i < G_N_ELEMENTS (multiarch_tuples) - 1; i++)
{
GList *libraries = NULL;
......@@ -615,6 +619,111 @@ main (int argc,
json_builder_end_object (builder);
json_builder_set_member_name (builder, "egl");
json_builder_begin_object (builder);
json_builder_set_member_name (builder, "icds");
json_builder_begin_array (builder);
icds = srt_system_info_list_egl_icds (info, multiarch_tuples);
for (icd_iter = icds; icd_iter != NULL; icd_iter = icd_iter->next)
{
json_builder_begin_object (builder);
json_builder_set_member_name (builder, "json_path");
json_builder_add_string_value (builder,
srt_egl_icd_get_json_path (icd_iter->data));
if (srt_egl_icd_check_error (icd_iter->data, &error))
{
const gchar *library;
gchar *tmp;
library = srt_egl_icd_get_library_path (icd_iter->data);
json_builder_set_member_name (builder, "library_path");
json_builder_add_string_value (builder, library);
tmp = srt_egl_icd_resolve_library_path (icd_iter->data);
if (g_strcmp0 (library, tmp) != 0)
{
json_builder_set_member_name (builder, "dlopen");
json_builder_add_string_value (builder, tmp);
}
g_free (tmp);
}
else
{
json_builder_set_member_name (builder, "error-domain");
json_builder_add_string_value (builder,
g_quark_to_string (error->domain));
json_builder_set_member_name (builder, "error-code");
json_builder_add_int_value (builder, error->code);
json_builder_set_member_name (builder, "error");
json_builder_add_string_value (builder, error->message);
g_clear_error (&error);
}
json_builder_end_object (builder);
}
g_list_free_full (icds, g_object_unref);
json_builder_end_array (builder); // egl.icds
json_builder_end_object (builder); // egl
json_builder_set_member_name (builder, "vulkan");
json_builder_begin_object (builder);
json_builder_set_member_name (builder, "icds");
json_builder_begin_array (builder);
icds = srt_system_info_list_vulkan_icds (info, multiarch_tuples);
for (icd_iter = icds; icd_iter != NULL; icd_iter = icd_iter->next)
{
json_builder_begin_object (builder);
json_builder_set_member_name (builder, "json_path");
json_builder_add_string_value (builder,
srt_vulkan_icd_get_json_path (icd_iter->data));
if (srt_vulkan_icd_check_error (icd_iter->data, &error))
{
const gchar *library;
gchar *tmp;
library = srt_vulkan_icd_get_library_path (icd_iter->data);
json_builder_set_member_name (builder, "library_path");
json_builder_add_string_value (builder, library);
json_builder_set_member_name (builder, "api_version");
json_builder_add_string_value (builder,
srt_vulkan_icd_get_api_version (icd_iter->data));
tmp = srt_vulkan_icd_resolve_library_path (icd_iter->data);
if (g_strcmp0 (library, tmp) != 0)
{
json_builder_set_member_name (builder, "dlopen");
json_builder_add_string_value (builder, tmp);
}
g_free (tmp);
}
else
{
json_builder_set_member_name (builder, "error-domain");
json_builder_add_string_value (builder,
g_quark_to_string (error->domain));
json_builder_set_member_name (builder, "error-code");
json_builder_add_int_value (builder, error->code);
json_builder_set_member_name (builder, "error");
json_builder_add_string_value (builder, error->message);
g_clear_error (&error);
}
json_builder_end_object (builder);
}
g_list_free_full (icds, g_object_unref);
json_builder_end_array (builder); // vulkan.icds
json_builder_end_object (builder); // vulkan
json_builder_end_object (builder); // End global object
JsonNode *root = json_builder_get_root (builder);
......
......@@ -294,6 +294,53 @@ keys:
**error**
: A human-readable error message.
**egl**
: An object describing EGL support. Currently the only key is
**icds**. Its value is an array of objects describing ICDs,
with the following keys and values if the metadata was loaded
successfully:
**json_path**
: Absolute path to the JSON file describing the ICD
**library_path**
: The library as described in the JSON file: either an absolute
path, a path relative to the JSON file containing at least one **/**,
or a bare library name to be loaded from the system default library
search path.
**dlopen**
: The name to pass to **dlopen**(3), if it differs from
**library_path**. This key/value pair is omitted if it would
be the same as **library_path**. Currently, it is only shown
if the **library_path** is relative, in which case **dlopen**
is an absolute path formed by prefixing the directory part
of **json_path** to **library_path**.
or the following keys and values if the metadata failed to load:
**json_path**
: Absolute path to the JSON file describing the ICD
**error-domain**
: A machine-readable string indicating a category of errors.
**error-code**
: A small integer indicating a specific error. Its meaning depends
on the **error-domain**.
**error**
: A human-readable error message.
**vulkan**
: An object describing Vulkan support. Currently the only key is
**icds**. Its value is an array of objects describing ICDs,
with the same keys and values as for EGL ICDs, plus one extra key:
**api_version**
: Vulkan API version implemented by this ICD as a dotted-decimal
string, for example **1.1.90**
# EXIT STATUS
0
......
......@@ -128,3 +128,7 @@ _srt_graphics_rendering_interface_string (SrtRenderingInterface rendering_interf
#endif
GList *_srt_load_egl_icds (gchar **envp,
const char * const *multiarch_tuples);
GList *_srt_load_vulkan_icds (gchar **envp,
const char * const *multiarch_tuples);
This diff is collapsed.
......@@ -109,6 +109,53 @@ const char *srt_graphics_get_renderer_string (SrtGraphics *self);
const char *srt_graphics_get_messages (SrtGraphics *self);
gchar *srt_graphics_dup_parameters_string (SrtGraphics *self);
typedef struct _SrtEglIcd SrtEglIcd;
typedef struct _SrtEglIcdClass SrtEglIcdClass;
#define SRT_TYPE_EGL_ICD (srt_egl_icd_get_type ())
#define SRT_EGL_ICD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SRT_TYPE_EGL_ICD, SrtEglIcd))
#define SRT_EGL_ICD_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST ((cls), SRT_TYPE_EGL_ICD, SrtEglIcdClass))
#define SRT_IS_EGL_ICD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SRT_TYPE_EGL_ICD))
#define SRT_IS_EGL_ICD_CLASS(cls) (G_TYPE_CHECK_CLASS_TYPE ((cls), SRT_TYPE_EGL_ICD))
#define SRT_EGL_ICD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), SRT_TYPE_EGL_ICD, SrtEglIcdClass)
GType srt_egl_icd_get_type (void);
gboolean srt_egl_icd_check_error (SrtEglIcd *self,
GError **error);
const gchar *srt_egl_icd_get_json_path (SrtEglIcd *self);
const gchar *srt_egl_icd_get_library_path (SrtEglIcd *self);
gchar *srt_egl_icd_resolve_library_path (SrtEglIcd *self);
SrtEglIcd *srt_egl_icd_new_replace_library_path (SrtEglIcd *self,
const char *path);
gboolean srt_egl_icd_write_to_file (SrtEglIcd *self,
const char *path,
GError **error);
typedef struct _SrtVulkanIcd SrtVulkanIcd;
typedef struct _SrtVulkanIcdClass SrtVulkanIcdClass;
#define SRT_TYPE_VULKAN_ICD (srt_vulkan_icd_get_type ())
#define SRT_VULKAN_ICD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SRT_TYPE_VULKAN_ICD, SrtVulkanIcd))
#define SRT_VULKAN_ICD_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST ((cls), SRT_TYPE_VULKAN_ICD, SrtVulkanIcdClass))
#define SRT_IS_VULKAN_ICD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SRT_TYPE_VULKAN_ICD))
#define SRT_IS_VULKAN_ICD_CLASS(cls) (G_TYPE_CHECK_CLASS_TYPE ((cls), SRT_TYPE_VULKAN_ICD))
#define SRT_VULKAN_ICD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), SRT_TYPE_VULKAN_ICD, SrtVulkanIcdClass)
GType srt_vulkan_icd_get_type (void);
gboolean srt_vulkan_icd_check_error (SrtVulkanIcd *self,
GError **error);
const gchar *srt_vulkan_icd_get_api_version (SrtVulkanIcd *self);
const gchar *srt_vulkan_icd_get_json_path (SrtVulkanIcd *self);
const gchar *srt_vulkan_icd_get_library_path (SrtVulkanIcd *self);
gchar *srt_vulkan_icd_resolve_library_path (SrtVulkanIcd *self);
SrtVulkanIcd *srt_vulkan_icd_new_replace_library_path (SrtVulkanIcd *self,
const char *path);
gboolean srt_vulkan_icd_write_to_file (SrtVulkanIcd *self,
const char *path,
GError **error);
#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SrtGraphics, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SrtEglIcd, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SrtVulkanIcd, g_object_unref)
#endif
......@@ -115,6 +115,13 @@ struct _SrtSystemInfo
gchar *version;
SrtRuntimeIssues issues;
} runtime;
struct
{
GList *egl;
GList *vulkan;
gboolean have_egl;
gboolean have_vulkan;
} icds;
SrtTestFlags test_flags;
Tristate can_write_uinput;
/* (element-type Abi) */
......@@ -317,11 +324,26 @@ forget_steam (SrtSystemInfo *self)
g_clear_pointer (&self->steam.bin32, g_free);
}
/*
* Forget any cached information about ICDs.
*/
static void
forget_icds (SrtSystemInfo *self)
{
self->icds.have_egl = FALSE;
g_list_free_full (self->icds.egl, g_object_unref);
self->icds.egl = NULL;
self->icds.have_vulkan = FALSE;
g_list_free_full (self->icds.vulkan, g_object_unref);
self->icds.vulkan = NULL;
}
static void
srt_system_info_finalize (GObject *object)
{
SrtSystemInfo *self = SRT_SYSTEM_INFO (object);
forget_icds (self);
forget_locales (self);
forget_runtime (self);
forget_steam (self);
......@@ -1430,3 +1452,101 @@ srt_system_info_set_test_flags (SrtSystemInfo *self,
g_return_if_fail (SRT_IS_SYSTEM_INFO (self));
self->test_flags = flags;
}
/**
* srt_system_info_list_egl_icds:
*
* List the available EGL ICDs, using the same search paths as GLVND.
*
* This function is not architecture-specific and may return a mixture
* of ICDs for more than one architecture or ABI, because the way the
* GLVND EGL loader works is to read a single search path for metadata
* describing ICDs, then filter out the ones that are for the wrong
* architecture at load time.
*
* Some of the entries in the result might describe a bare SONAME in the
* standard library search path, which might exist for any or all
* architectures simultaneously (this is the most common approach for EGL).
* Other entries might describe the relative or absolute path to a
* specific library, which will only be usable for the architecture for
* which it was compiled.
*
* @multiarch_tuples is used if running in a Flatpak environment, to
* match the search paths used by the freedesktop.org runtime's patched
* GLVND.
*
* Returns: (transfer full) (element-type SrtEglIcd): A list of
* opaque #SrtEglIcd objects. Free with
* `g_list_free_full(icds, srt_egl_icd_unref)`.
*/
GList *
srt_system_info_list_egl_icds (SrtSystemInfo *self,
const char * const *multiarch_tuples)
{
GList *ret = NULL;
const GList *iter;
g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), NULL);
if (!self->icds.have_egl)
{
g_assert (self->icds.egl == NULL);
self->icds.egl = _srt_load_egl_icds (self->env, multiarch_tuples);
self->icds.have_egl = TRUE;
}
for (iter = self->icds.egl; iter != NULL; iter = iter->next)
ret = g_list_prepend (ret, g_object_ref (iter->data));
return g_list_reverse (ret);
}
/**
* srt_system_info_list_vulkan_icds:
*
* List the available Vulkan ICDs, using the same search paths as the
* reference vulkan-loader.
*
* This function is not architecture-specific and may return a mixture
* of ICDs for more than one architecture or ABI, because the way the
* reference vulkan-loader works is to read a single search path for
* metadata describing ICDs, then filter out the ones that are for the
* wrong architecture at load time.
*
* Some of the entries in the result might describe a bare SONAME in the
* standard library search path, which might exist for any or all
* architectures simultaneously (for example, this approach is used for
* the NVIDIA binary driver on Debian systems). Other entries might
* describe the relative or absolute path to a specific library, which
* will only be usable for the architecture for which it was compiled
* (for example, this approach is used in Mesa).
*
* @multiarch_tuples is used if running in a Flatpak environment, to
* match the search paths used by the freedesktop.org runtime's patched
* vulkan-loader.
*
* Returns: (transfer full) (element-type SrtVulkanIcd): A list of
* opaque #SrtVulkanIcd objects. Free with
* `g_list_free_full(icds, srt_vulkan_icd_unref)`.
*/
GList *
srt_system_info_list_vulkan_icds (SrtSystemInfo *self,
const char * const *multiarch_tuples)
{
GList *ret = NULL;
const GList *iter;
g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), NULL);
if (!self->icds.have_vulkan)
{
g_assert (self->icds.vulkan == NULL);
self->icds.vulkan = _srt_load_vulkan_icds (self->env, multiarch_tuples);
self->icds.have_vulkan = TRUE;
}
for (iter = self->icds.vulkan; iter != NULL; iter = iter->next)
ret = g_list_prepend (ret, g_object_ref (iter->data));
return g_list_reverse (ret);
}
......@@ -87,6 +87,11 @@ SrtGraphicsIssues srt_system_info_check_graphics (SrtSystemInfo *self,
GList * srt_system_info_check_all_graphics (SrtSystemInfo *self,
const char *multiarch_tuple);
GList *srt_system_info_list_egl_icds (SrtSystemInfo *self,
const char * const *multiarch_tuples);
GList *srt_system_info_list_vulkan_icds (SrtSystemInfo *self,
const char * const *multiarch_tuples);
void srt_system_info_set_environ (SrtSystemInfo *self,
gchar * const *env);
void srt_system_info_set_helpers_path (SrtSystemInfo *self,
......
{
"ICD": {
"api_version": "1.1.102",
"library_path": "/usr/lib/x86_64-linux-gnu/libvulkan_intel.so"
},
"file_format_version": "1.1.0"
}
{
"ICD": {
"api_version": "1.1.102",
"library_path": "/usr/lib/x86_64-linux-gnu/libvulkan_intel.so"
},
"file_format_version": "2.0.0"
}
{
"ICD": {
"library_path": "/usr/lib/x86_64-linux-gnu/libvulkan_intel.so"
},
"file_format_version": "1.0.0"
}
hello, world!
{
"file_format_version" : "1.0.0",
"ICD" : {
"library_path" : "/opt/libEGL_myvendor.so"
}
}
{
"ICD": {
"api_version": "1.2.3",
"library_path": "libvulkan_basename.so"
},
"file_format_version": "1.0.0"
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment