diff --git a/bin/system-info.c b/bin/system-info.c index 42fd5b1f0a0f58f2bfe2b70553a9abfe53e99c9c..2be787c1f05fc359dde8aefd05863d3e7a088747 100644 --- a/bin/system-info.c +++ b/bin/system-info.c @@ -244,6 +244,29 @@ jsonify_graphics_issues (JsonBuilder *builder, json_builder_add_string_value (builder, "software-rendering"); } +static void +jsonify_graphics_library_vendor (JsonBuilder *builder, + SrtGraphicsLibraryVendor vendor) +{ + if (vendor == SRT_GRAPHICS_LIBRARY_VENDOR_UNKNOWN) + json_builder_add_string_value (builder, "unknown"); + + else if (vendor == SRT_GRAPHICS_LIBRARY_VENDOR_GLVND) + json_builder_add_string_value (builder, "glvnd"); + + else if (vendor == SRT_GRAPHICS_LIBRARY_VENDOR_UNKNOWN_NON_GLVND) + json_builder_add_string_value (builder, "unknown-non-glvnd"); + + else if (vendor == SRT_GRAPHICS_LIBRARY_VENDOR_MESA) + json_builder_add_string_value (builder, "mesa"); + + else if (vendor == SRT_GRAPHICS_LIBRARY_VENDOR_NVIDIA) + json_builder_add_string_value (builder, "nvidia"); + + else + json_builder_add_string_value (builder, "unexpected-vendor"); +} + static void jsonify_steam_issues (JsonBuilder *builder, SrtSteamIssues issues) @@ -405,6 +428,7 @@ print_graphics_details(JsonBuilder *builder, { gchar *parameters = srt_graphics_dup_parameters_string (g->data); const char *messages; + SrtGraphicsLibraryVendor library_vendor; json_builder_set_member_name (builder, parameters); json_builder_begin_object (builder); @@ -421,6 +445,12 @@ print_graphics_details(JsonBuilder *builder, json_builder_add_string_value (builder, srt_graphics_get_renderer_string (g->data)); json_builder_set_member_name (builder, "version"); json_builder_add_string_value (builder, srt_graphics_get_version_string (g->data)); + if (srt_graphics_get_rendering_interface (g->data) != SRT_RENDERING_INTERFACE_VULKAN) + { + json_builder_set_member_name (builder, "library-vendor"); + srt_graphics_library_is_vendor_neutral (g->data, &library_vendor); + jsonify_graphics_library_vendor (builder, library_vendor); + } if (srt_graphics_get_issues (g->data) != SRT_GRAPHICS_ISSUES_NONE) { diff --git a/steam-runtime-tools/graphics-internal.h b/steam-runtime-tools/graphics-internal.h index 421587a9a65b4a1fa77768b591c7ca9601f072d9..a4a5c67df1a599f5c4d09f4832dd12a4a2acf61b 100644 --- a/steam-runtime-tools/graphics-internal.h +++ b/steam-runtime-tools/graphics-internal.h @@ -46,6 +46,7 @@ static inline SrtGraphics *_srt_graphics_new (const char *multiarch_tuple, SrtWindowSystem window_system, SrtRenderingInterface rendering_interface, + SrtGraphicsLibraryVendor library_vendor, const gchar *renderer_string, const gchar *version_string, SrtGraphicsIssues issues, @@ -77,6 +78,7 @@ static inline SrtGraphics * _srt_graphics_new (const char *multiarch_tuple, SrtWindowSystem window_system, SrtRenderingInterface rendering_interface, + SrtGraphicsLibraryVendor library_vendor, const gchar *renderer_string, const gchar *version_string, SrtGraphicsIssues issues, @@ -86,6 +88,7 @@ _srt_graphics_new (const char *multiarch_tuple, return g_object_new (SRT_TYPE_GRAPHICS, "multiarch-tuple", multiarch_tuple, "issues", issues, + "library-vendor", library_vendor, "window-system", window_system, "rendering-interface", rendering_interface, "renderer-string", renderer_string, diff --git a/steam-runtime-tools/graphics.c b/steam-runtime-tools/graphics.c index 558c53e38a4ea41914ad8ff2b6ab556854cc33eb..9c63a9206c67c012e839643d73989f18969235a8 100644 --- a/steam-runtime-tools/graphics.c +++ b/steam-runtime-tools/graphics.c @@ -71,6 +71,7 @@ struct _SrtGraphics SrtWindowSystem window_system; SrtRenderingInterface rendering_interface; SrtGraphicsIssues issues; + SrtGraphicsLibraryVendor library_vendor; gchar *messages; gchar *renderer_string; gchar *version_string; @@ -85,6 +86,7 @@ struct _SrtGraphicsClass enum { PROP_0, PROP_ISSUES, + PROP_LIBRARY_VENDOR, PROP_MESSAGES, PROP_MULTIARCH_TUPLE, PROP_WINDOW_SYSTEM, @@ -115,6 +117,10 @@ srt_graphics_get_property (GObject *object, g_value_set_flags (value, self->issues); break; + case PROP_LIBRARY_VENDOR: + g_value_set_enum (value, self->library_vendor); + break; + case PROP_MESSAGES: g_value_set_string (value, self->messages); break; @@ -161,6 +167,12 @@ srt_graphics_set_property (GObject *object, self->issues = g_value_get_flags (value); break; + case PROP_LIBRARY_VENDOR: + /* Construct-only */ + g_return_if_fail (self->library_vendor == 0); + self->library_vendor = g_value_get_enum (value); + break; + case PROP_MESSAGES: /* Construct-only */ g_return_if_fail (self->messages == NULL); @@ -240,6 +252,12 @@ srt_graphics_class_init (SrtGraphicsClass *cls) G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + properties[PROP_LIBRARY_VENDOR] = + g_param_spec_enum ("library-vendor", "Library vendor", "Which library vendor is currently in use.", + SRT_TYPE_GRAPHICS_LIBRARY_VENDOR, SRT_GRAPHICS_LIBRARY_VENDOR_UNKNOWN, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + properties[PROP_MESSAGES] = g_param_spec_string ("messages", "Messages", "Diagnostic messages produced while checking this " @@ -541,7 +559,6 @@ _argv_for_graphics_test (const char *helpers_path, return argv; } - static GPtrArray * _argv_for_check_vulkan (const char *helpers_path, SrtTestFlags test_flags, @@ -576,6 +593,94 @@ _argv_for_check_vulkan (const char *helpers_path, return argv; } +/** + * _srt_check_library_vendor: + * @multiarch_tuple: A multiarch tuple to check e.g. i386-linux-gnu + * @window_system: The window system to check. + * @rendering_interface: The graphics renderer to check. + * + * Return whether the entry-point library for this graphics stack is vendor-neutral or + * vendor-specific, and if vendor-specific, attempt to guess the vendor. + * + * For newer GLX and EGL graphics stacks (since 2017-2018) the entry-point library is + * provided by GLVND, a vendor-neutral dispatch library that loads vendor-specific ICDs. + * This function returns %SRT_GRAPHICS_DRIVER_VENDOR_GLVND. + * + * For older GLX and EGL graphics stacks, the entry-point library libGL.so.1 or libEGL.so.1 + * is part of a particular vendor's graphics library, usually Mesa or NVIDIA. This function + * attempts to determine which one. + * + * For Vulkan the entry-point library libvulkan.so.1 is always vendor-neutral + * (similar to GLVND), so this function is not useful. It always returns + * %SRT_GRAPHICS_DRIVER_VENDOR_UNKNOWN. + * + * Returns: the graphics library vendor currently in use, or + * %SRT_GRAPHICS_LIBRARY_VENDOR_UNKNOWN if the rendering interface is + * %SRT_RENDERING_INTERFACE_VULKAN or if there was a problem loading the library. + */ +static SrtGraphicsLibraryVendor +_srt_check_library_vendor (const char *multiarch_tuple, + SrtWindowSystem window_system, + SrtRenderingInterface rendering_interface) +{ + SrtGraphicsLibraryVendor library_vendor = SRT_GRAPHICS_LIBRARY_VENDOR_UNKNOWN; + const gchar *soname = NULL; + SrtLibrary *library = NULL; + SrtLibraryIssues issues; + const char * const *dependencies; + + /* Vulkan is always vendor-neutral, so it doesn't make sense to check it. We simply return + * SRT_GRAPHICS_LIBRARY_VENDOR_UNKNOWN */ + if (rendering_interface == SRT_RENDERING_INTERFACE_VULKAN) + goto out; + + switch (window_system) + { + case SRT_WINDOW_SYSTEM_GLX: + case SRT_WINDOW_SYSTEM_X11: + soname = "libGL.so.1"; + break; + + case SRT_WINDOW_SYSTEM_EGL_X11: + soname = "libEGL.so.1"; + break; + + default: + g_return_val_if_reached (SRT_GRAPHICS_LIBRARY_VENDOR_UNKNOWN); + } + + issues = srt_check_library_presence (soname, multiarch_tuple, NULL, SRT_LIBRARY_SYMBOLS_FORMAT_PLAIN, &library); + + if ((issues & SRT_LIBRARY_ISSUES_CANNOT_LOAD) != 0) + goto out; + + dependencies = srt_library_get_dependencies (library); + library_vendor = SRT_GRAPHICS_LIBRARY_VENDOR_UNKNOWN_NON_GLVND; + + for (gsize i = 0; dependencies[i] != NULL; i++) + { + if (strstr (dependencies[i], "/libGLdispatch.so.") != NULL) + { + library_vendor = SRT_GRAPHICS_LIBRARY_VENDOR_GLVND; + break; + } + if (strstr (dependencies[i], "/libglapi.so.") != NULL) + { + library_vendor = SRT_GRAPHICS_LIBRARY_VENDOR_MESA; + break; + } + if (strstr (dependencies[i], "/libnvidia-") != NULL) + { + library_vendor = SRT_GRAPHICS_LIBRARY_VENDOR_NVIDIA; + break; + } + } + +out: + g_clear_pointer (&library, g_object_unref); + return library_vendor; +} + /** * _srt_check_graphics: * @helpers_path: An optional path to find wflinfo helpers, PATH is used if null. @@ -611,6 +716,7 @@ _srt_check_graphics (const char *helpers_path, GStrv my_environ = NULL; const gchar *ld_preload; gchar *filtered_preload = NULL; + SrtGraphicsLibraryVendor library_vendor = SRT_GRAPHICS_LIBRARY_VENDOR_UNKNOWN; gboolean parse_wflinfo = (rendering_interface != SRT_RENDERING_INTERFACE_VULKAN); g_return_val_if_fail (details_out == NULL || *details_out == NULL, SRT_GRAPHICS_ISSUES_INTERNAL_ERROR); @@ -637,6 +743,8 @@ _srt_check_graphics (const char *helpers_path, my_environ = g_environ_setenv (my_environ, "LD_PRELOAD", filtered_preload, TRUE); } + library_vendor = _srt_check_library_vendor (multiarch_tuple, window_system, rendering_interface); + if (!g_spawn_sync (NULL, /* working directory */ (gchar **) argv->pdata, my_environ, /* envp */ @@ -747,6 +855,7 @@ out: *details_out = _srt_graphics_new (multiarch_tuple, window_system, rendering_interface, + library_vendor, renderer_string, version_string, issues, @@ -781,6 +890,31 @@ srt_graphics_get_issues (SrtGraphics *self) return self->issues; } +/** + * srt_graphics_library_is_vendor_neutral: + * @self: A #SrtGraphics object + * @vendor_out: (out) (optional): Used to return a #SrtGraphicsLibraryVendor object + * representing whether the entry-point library for this graphics stack is vendor-neutral + * or vendor-specific, and if vendor-specific, attempt to guess the vendor + * + * Return whether the entry-point library for this graphics stack is vendor-neutral or vendor-specific. + * Vulkan is always vendor-neutral, so this function will always return %TRUE for it. + * + * Returns: %TRUE if the graphics library is vendor-neutral, %FALSE otherwise. + */ +gboolean +srt_graphics_library_is_vendor_neutral (SrtGraphics *self, + SrtGraphicsLibraryVendor *vendor_out) +{ + g_return_val_if_fail (SRT_IS_GRAPHICS (self), FALSE); + + if (vendor_out != NULL) + *vendor_out = self->library_vendor; + + return (self->rendering_interface == SRT_RENDERING_INTERFACE_VULKAN || + self->library_vendor == SRT_GRAPHICS_LIBRARY_VENDOR_GLVND); +} + /** * srt_graphics_get_multiarch_tuple: * @self: a graphics object diff --git a/steam-runtime-tools/graphics.h b/steam-runtime-tools/graphics.h index d241e6c9d370180ae8e77d15646cad5a1b9b3a1e..8465a9987e90cc40aebf79a55ff478aa87a6c8aa 100644 --- a/steam-runtime-tools/graphics.h +++ b/steam-runtime-tools/graphics.h @@ -71,6 +71,24 @@ typedef enum SRT_GRAPHICS_ISSUES_CANNOT_DRAW = (1 << 4) } SrtGraphicsIssues; +/** + * SrtGraphicsLibraryVendor: + * @SRT_GRAPHICS_LIBRARY_VENDOR_UNKNOWN: Unable to check the graphics driver vendor + * @SRT_GRAPHICS_LIBRARY_VENDOR_GLVND: The graphics driver is the vendor-neutral GLVND + * @SRT_GRAPHICS_LIBRARY_VENDOR_UNKNOWN_NON_GLVND: The graphics driver is non-GLVND, + * but the exact vendor is unknown + * @SRT_GRAPHICS_LIBRARY_VENDOR_MESA: The graphics driver is the mesa non-GLVND + * @SRT_GRAPHICS_LIBRARY_VENDOR_NVIDIA: The graphics driver is the Nvidia non-GLVND + */ +typedef enum +{ + SRT_GRAPHICS_LIBRARY_VENDOR_UNKNOWN, + SRT_GRAPHICS_LIBRARY_VENDOR_GLVND, + SRT_GRAPHICS_LIBRARY_VENDOR_UNKNOWN_NON_GLVND, + SRT_GRAPHICS_LIBRARY_VENDOR_MESA, + SRT_GRAPHICS_LIBRARY_VENDOR_NVIDIA, +} SrtGraphicsLibraryVendor; + /** * SrtWindowSystem: * @SRT_WINDOW_SYSTEM_X11: X11 window system, with GL: equivalent to GLX; with GLES: equivalent to EGL_X11; with Vulkan: use X11 @@ -104,6 +122,8 @@ typedef enum const char *srt_graphics_get_multiarch_tuple (SrtGraphics *self); SrtGraphicsIssues srt_graphics_get_issues (SrtGraphics *self); +gboolean srt_graphics_library_is_vendor_neutral (SrtGraphics *self, + SrtGraphicsLibraryVendor *vendor_out); SrtWindowSystem srt_graphics_get_window_system (SrtGraphics *self); SrtRenderingInterface srt_graphics_get_rendering_interface (SrtGraphics *self); const char *srt_graphics_get_version_string (SrtGraphics *self); diff --git a/tests/graphics.c b/tests/graphics.c index 2ea2482476d15f146d030a9ed724e7a0a5c8fc89..3cd18da32d5fbb920e162f1e4589326c1304f7eb 100644 --- a/tests/graphics.c +++ b/tests/graphics.c @@ -194,6 +194,8 @@ test_object (Fixture *f, { SrtGraphics *graphics; SrtGraphicsIssues issues; + SrtGraphicsLibraryVendor library_vendor; + gboolean vendor_neutral; gchar *messages; gchar *tuple; gchar *renderer; @@ -202,6 +204,7 @@ test_object (Fixture *f, graphics = _srt_graphics_new("mock-good", SRT_WINDOW_SYSTEM_GLX, SRT_RENDERING_INTERFACE_GL, + SRT_GRAPHICS_LIBRARY_VENDOR_GLVND, SRT_TEST_GOOD_GRAPHICS_RENDERER, SRT_TEST_GOOD_GRAPHICS_VERSION, SRT_GRAPHICS_ISSUES_NONE, @@ -215,14 +218,19 @@ test_object (Fixture *f, g_assert_cmpstr (srt_graphics_get_version_string (graphics), ==, SRT_TEST_GOOD_GRAPHICS_VERSION); g_assert_cmpstr (srt_graphics_get_messages (graphics), ==, NULL); + vendor_neutral = srt_graphics_library_is_vendor_neutral (graphics, &library_vendor); + g_assert_cmpint (library_vendor, ==, SRT_GRAPHICS_LIBRARY_VENDOR_GLVND); + g_assert_true (vendor_neutral); g_object_get (graphics, "messages", &messages, "multiarch-tuple", &tuple, "issues", &issues, + "library-vendor", &library_vendor, "renderer-string", &renderer, "version-string", &version, NULL); g_assert_cmpint (issues, ==, SRT_GRAPHICS_ISSUES_NONE); + g_assert_cmpint (library_vendor, ==, SRT_GRAPHICS_LIBRARY_VENDOR_GLVND); g_assert_cmpstr (messages, ==, NULL); g_assert_cmpstr (tuple, ==, "mock-good"); g_assert_cmpstr (renderer, ==, SRT_TEST_GOOD_GRAPHICS_RENDERER); @@ -287,6 +295,8 @@ test_bad_graphics (Fixture *f, { SrtGraphics *graphics = NULL; SrtGraphicsIssues issues; + SrtGraphicsLibraryVendor library_vendor; + gboolean vendor_neutral; gchar *messages; gchar *tuple; gchar *renderer; @@ -307,14 +317,22 @@ test_bad_graphics (Fixture *f, NULL); g_assert_cmpstr (srt_graphics_get_messages (graphics), ==, "Waffle error: 0x2 WAFFLE_ERROR_UNKNOWN: XOpenDisplay failed\n"); + /* We used "mock-bad" for the architecture so, when checking the library vendor, + * we will not be able to call the helper `mock-bad-check-library`. + * For this reason we expect %SRT_GRAPHICS_LIBRARY_VENDOR_UNKNOWN */ + vendor_neutral = srt_graphics_library_is_vendor_neutral (graphics, &library_vendor); + g_assert_cmpint (library_vendor, ==, SRT_GRAPHICS_LIBRARY_VENDOR_UNKNOWN); + g_assert_false (vendor_neutral); g_object_get (graphics, "messages", &messages, "multiarch-tuple", &tuple, "issues", &issues, + "library-vendor", &library_vendor, "renderer-string", &renderer, "version-string", &version, NULL); g_assert_cmpint (issues, ==, SRT_GRAPHICS_ISSUES_CANNOT_LOAD); + g_assert_cmpint (library_vendor, ==, SRT_GRAPHICS_LIBRARY_VENDOR_UNKNOWN); g_assert_cmpstr (messages, ==, "Waffle error: 0x2 WAFFLE_ERROR_UNKNOWN: XOpenDisplay failed\n"); g_assert_cmpstr (tuple, ==, "mock-bad");