Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions .github/workflows/Linux-samples-gfx.yml
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,54 @@ jobs:
make -C samples/gfx/vulkan_driver_pacing CC=x86_64-w64-mingw32-gcc RUNNER=wine check
make -C samples/gfx/vulkan_driver_pacing clean

vulkan-custom-driver:
name: Android custom Vulkan driver packages and crash state
runs-on: ubuntu-latest
timeout-minutes: 15
env:
APT_PKGS: gcc-mingw-w64-x86-64 wine64

steps:
- name: Checkout
uses: actions/checkout@v5

- name: Apt cache key
id: apt
run: sh .github/workflows/scripts/apt-cache-key.sh "$APT_PKGS"

- name: Restore apt cache
uses: actions/cache/restore@v4
with:
path: ~/.apt-cache
key: ${{ steps.apt.outputs.key }}
restore-keys: ${{ steps.apt.outputs.prefix }}

- name: Install dependencies
timeout-minutes: 15
run: sh .github/workflows/scripts/apt-install.sh $APT_PKGS

- name: Save apt cache
if: env.APT_CACHE_SAVE == '1'
uses: actions/cache/save@v4
with:
path: ~/.apt-cache
key: ${{ steps.apt.outputs.key }}

- name: Regression test (Linux, ASan + UBSan)
run: |
# Package checks, the extraction key, the sticky crash state,
# the session's driver choices and the soname patch, fed from
# buffers; the loader itself is Android-only.
UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
make -C samples/gfx/vulkan_custom_driver SANITIZER=address,undefined check
make -C samples/gfx/vulkan_custom_driver clean

- name: Regression test (mingw-w64, Wine)
run: |
. "$GITHUB_WORKSPACE/.github/workflows/scripts/wine-server.sh"
make -C samples/gfx/vulkan_custom_driver CC=x86_64-w64-mingw32-gcc RUNNER=wine check
make -C samples/gfx/vulkan_custom_driver clean

vulkan-upscaler:
name: Vulkan temporal upscaler glue (lavapipe, stand-in NGX, FSR and XeSS)
runs-on: ubuntu-latest
Expand Down
11 changes: 11 additions & 0 deletions configuration.c
Original file line number Diff line number Diff line change
Expand Up @@ -2208,6 +2208,9 @@ static struct config_path_setting *populate_settings_path(
#endif
SETTING_PATH("video_filter", settings->paths.path_softfilter_plugin, false, NULL, true);
SETTING_PATH("video_font_path", settings->paths.path_font, false, NULL, true);
#ifdef HAVE_VULKAN_CUSTOM_DRIVER
SETTING_PATH("video_vulkan_driver_path", settings->paths.path_vulkan_driver, false, NULL, true);
#endif
SETTING_PATH("video_record_config", settings->paths.path_record_config, false, NULL, true);
SETTING_PATH("video_stream_config", settings->paths.path_stream_config, false, NULL, true);
SETTING_PATH("video_stream_url", settings->paths.path_stream_url, false, NULL, true);
Expand Down Expand Up @@ -2506,6 +2509,7 @@ static const struct config_bool_row config_bool_rows[] = {
#include "settings/settings_def_input_bind_timeouts.h"
#include "settings/settings_def_video_hdr_toggles.h"
#include "settings/settings_def_video_upscaler.h"
#include "settings/settings_def_vulkan_driver_path.h"
#include "settings/settings_def_video_stereo.h"
#include "settings/settings_def_video_headset.h"
#include "settings/settings_def_rewind.h"
Expand Down Expand Up @@ -3344,6 +3348,7 @@ static const struct config_float_row config_float_rows[] = {
#include "settings/settings_def_input_bind_timeouts.h"
#include "settings/settings_def_video_hdr_toggles.h"
#include "settings/settings_def_video_upscaler.h"
#include "settings/settings_def_vulkan_driver_path.h"
#include "settings/settings_def_video_stereo.h"
#include "settings/settings_def_video_headset.h"
#include "settings/settings_def_rewind.h"
Expand Down Expand Up @@ -3988,6 +3993,7 @@ static const struct config_uint_row config_uint_rows[] = {
#include "settings/settings_def_input_bind_timeouts.h"
#include "settings/settings_def_video_hdr_toggles.h"
#include "settings/settings_def_video_upscaler.h"
#include "settings/settings_def_vulkan_driver_path.h"
#include "settings/settings_def_video_stereo.h"
#include "settings/settings_def_video_headset.h"
#include "settings/settings_def_rewind.h"
Expand Down Expand Up @@ -4787,6 +4793,7 @@ static const struct config_int_row config_int_rows[] = {
#include "settings/settings_def_input_bind_timeouts.h"
#include "settings/settings_def_video_hdr_toggles.h"
#include "settings/settings_def_video_upscaler.h"
#include "settings/settings_def_vulkan_driver_path.h"
#include "settings/settings_def_video_stereo.h"
#include "settings/settings_def_video_headset.h"
#include "settings/settings_def_rewind.h"
Expand Down Expand Up @@ -5430,6 +5437,7 @@ static struct config_int_setting *populate_settings_int(
#include "settings/settings_def_input_bind_timeouts.h"
#include "settings/settings_def_video_hdr_toggles.h"
#include "settings/settings_def_video_upscaler.h"
#include "settings/settings_def_vulkan_driver_path.h"
#include "settings/settings_def_video_stereo.h"
#include "settings/settings_def_video_headset.h"
#include "settings/settings_def_rewind.h"
Expand Down Expand Up @@ -6452,6 +6460,9 @@ void config_set_defaults(settings_t *target)
*settings->paths.path_softfilter_plugin = '\0';

*settings->paths.path_audio_dsp_plugin = '\0';
#ifdef HAVE_VULKAN_CUSTOM_DRIVER
*settings->paths.path_vulkan_driver = '\0';
#endif

*settings->paths.log_dir = '\0';

Expand Down
3 changes: 3 additions & 0 deletions configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -1531,6 +1531,9 @@ typedef struct settings
char path_cheat_settings[PATH_MAX_LENGTH];
char path_font[PATH_MAX_LENGTH];
char path_rgui_theme_preset[PATH_MAX_LENGTH];
#ifdef HAVE_VULKAN_CUSTOM_DRIVER
char path_vulkan_driver[PATH_MAX_LENGTH];
#endif
char app_icon[PATH_MAX_LENGTH];

char browse_url[4096]; /* TODO/FIXME - check size */
Expand Down
28 changes: 28 additions & 0 deletions frontend/drivers/platform_unix.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@
#include <formats/rjson.h>
#endif

#ifdef HAVE_VULKAN_CUSTOM_DRIVER
#include "../../gfx/common/vulkan_custom_driver.h"
#endif

#ifdef ANDROID
static void frontend_unix_set_sustained_performance_mode(bool on);

Expand Down Expand Up @@ -3038,6 +3042,26 @@ static void frontend_unix_get_env(int *argc,
args->config_path = android_config_path;
}

#ifdef HAVE_VULKAN_CUSTOM_DRIVER
/* Safe mode, from the "Safe start" launcher shortcut. */
CALL_OBJ_METHOD_PARAM(env, jstr, obj, android_app->getStringExtra,
(*env)->NewStringUTF(env, "VULKAN_DRIVER"));

{
bool safe_mode = false;
if (android_app->getStringExtra && jstr)
{
const char *argv = (*env)->GetStringUTFChars(env, jstr, 0);

safe_mode = string_is_equal(argv, "system");
__android_log_print(ANDROID_LOG_INFO,
"RetroArch", "[ENV] Vulkan driver: \"%s\".\n", argv);
(*env)->ReleaseStringUTFChars(env, jstr, argv);
}
vulkan_custom_driver_set_safe_mode(safe_mode);
}
#endif

/* Current IME. */
CALL_OBJ_METHOD_PARAM(env, jstr, obj, android_app->getStringExtra,
(*env)->NewStringUTF(env, "IME"));
Expand Down Expand Up @@ -3976,6 +4000,10 @@ static void frontend_unix_init(void *data)
"hideKeyboard", "()V");
GET_METHOD_ID(env, android_app->setSecondDisplay, class,
"setSecondDisplay", "(II)V");
#ifdef HAVE_VULKAN_CUSTOM_DRIVER
GET_METHOD_ID(env, android_app->addSafeStartShortcut, class,
"addSafeStartShortcut", "()V");
#endif

CALL_BOOLEAN_METHOD(env, android_app->is_play_store_build,
android_app->activity->clazz, android_app->isPlayStoreBuild);
Expand Down
3 changes: 3 additions & 0 deletions frontend/drivers/platform_unix.h
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,9 @@ struct android_app
jmethodID showKeyboard;
jmethodID hideKeyboard;
jmethodID setSecondDisplay;
#ifdef HAVE_VULKAN_CUSTOM_DRIVER
jmethodID addSafeStartShortcut;
#endif

/* Written by the Android UI thread in onContentRectChanged(), read by
* the video thread in the context drivers, with no lock on either
Expand Down
129 changes: 129 additions & 0 deletions gfx/common/vulkan_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@
#include "vulkan_android_device.h"
#include "vulkan_android_cache.h"
#endif
#ifdef HAVE_VULKAN_CUSTOM_DRIVER
#include "vulkan_custom_driver.h"
#endif
#ifdef HAVE_TEMPORAL_UPSCALER
#include "vulkan_upscaler.h"
#include "../video_upscaler.h"
Expand Down Expand Up @@ -99,6 +102,10 @@
* Vulkan contexts (multi-window, etc.). The cached_device_vk
* mechanism assumes single-context ownership. */
static dylib_t vulkan_library;
#ifdef HAVE_VULKAN_CUSTOM_DRIVER
/* From vulkan_custom_driver_open(), which keeps it: never closed here. */
static bool vulkan_library_custom;
#endif
static VkInstance cached_instance_vk;
#if defined(_WIN32) && !defined(__WINRT__)
#include "win_vulkan_dxgi.h"
Expand Down Expand Up @@ -5276,6 +5283,76 @@ static void vulkan_context_openxr_init(gfx_ctx_vulkan_data_t *vk,
}
#endif

#ifdef HAVE_VULKAN_CUSTOM_DRIVER
/* A custom driver has to see a GPU. A Mesa one must also not report the
* system driver's ID: the hook loads that driver when the custom library
* fails to. Other vendors' builds may report that ID themselves. */
static bool vulkan_context_custom_driver_ok(gfx_ctx_vulkan_data_t *vk,
uint32_t api_version)
{
VkPhysicalDeviceDriverProperties driver;
VkPhysicalDeviceProperties2 props;
PFN_vkGetPhysicalDeviceProperties2 get_props2 = NULL;
VkPhysicalDevice gpu = VK_NULL_HANDLE;
uint32_t count = 1;
VkResult res = vkEnumeratePhysicalDevices(
vk->context.instance, &count, &gpu);

if ((res != VK_SUCCESS && res != VK_INCOMPLETE) || !count || !gpu)
{
RARCH_WARN("[Vulkan] Custom driver found no GPU.\n");
return false;
}
if (!vulkan_custom_driver_mesa())
{
RARCH_WARN("[Vulkan] Custom driver is not Mesa: its load cannot be confirmed.\n");
return true;
}
if (api_version >= VK_API_VERSION_1_1)
get_props2 = (PFN_vkGetPhysicalDeviceProperties2)
vulkan_symbol_wrapper_instance_proc_addr()(vk->context.instance,
"vkGetPhysicalDeviceProperties2");
if (!get_props2)
return true;
memset(&driver, 0, sizeof(driver));
memset(&props, 0, sizeof(props));
driver.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES;
props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
props.pNext = &driver;
get_props2(gpu, &props);
if (driver.driverID == VK_DRIVER_ID_QUALCOMM_PROPRIETARY)
{
RARCH_WARN("[Vulkan] Custom driver did not load; the system one did.\n");
return false;
}
return true;
}

/* The custom driver failed before its surface: init once more. Its path
* is now off for the session, so this one gets the system driver. */
static bool vulkan_context_init_system(gfx_ctx_vulkan_data_t *vk,
enum vulkan_wsi_type type)
{
if (vk->context.instance)
{
PFN_vkDestroyInstance destroy = (PFN_vkDestroyInstance)
vulkan_symbol_wrapper_instance_proc_addr()(vk->context.instance,
"vkDestroyInstance");
#ifdef VULKAN_DEBUG
if (vk->context.debug_callback)
vkDestroyDebugUtilsMessengerEXT(vk->context.instance,
vk->context.debug_callback, NULL);
vk->context.debug_callback = VK_NULL_HANDLE;
#endif
if (destroy)
destroy(vk->context.instance, NULL);
vk->context.instance = VK_NULL_HANDLE;
}
vulkan_custom_driver_failed();
return vulkan_context_init(vk, type);
}
#endif

bool vulkan_context_init(gfx_ctx_vulkan_data_t *vk,
enum vulkan_wsi_type type)
{
Expand All @@ -5285,6 +5362,9 @@ bool vulkan_context_init(gfx_ctx_vulkan_data_t *vk,
video_driver_state_t *video_st = video_state_get_ptr();
struct retro_hw_render_context_negotiation_interface_vulkan
*iface = (struct retro_hw_render_context_negotiation_interface_vulkan*)video_st->hw_render_context_negotiation;
#ifdef HAVE_VULKAN_CUSTOM_DRIVER
bool custom_driver = false;
#endif

if (iface && iface->interface_type != RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN)
{
Expand All @@ -5301,6 +5381,25 @@ bool vulkan_context_init(gfx_ctx_vulkan_data_t *vk,
vk->wsi_type = type;
vulkan_swapchain_settings_init(&vk->context.swapchain_settings);

#ifdef HAVE_VULKAN_CUSTOM_DRIVER
/* A kept instance keeps the loader it was made with. */
if (!cached_instance_vk)
{
dylib_t custom = vulkan_custom_driver_open();
if ( vulkan_library != custom
&& (custom || vulkan_library_custom))
{
if (vulkan_library && !vulkan_library_custom)
dylib_close(vulkan_library);
vulkan_library = custom;
}
vulkan_library_custom = (custom != NULL);
custom_driver = vulkan_library_custom;
}
else
vulkan_custom_driver_kept();
#endif

if (!vulkan_library)
{
#ifdef _WIN32
Expand Down Expand Up @@ -5345,6 +5444,10 @@ bool vulkan_context_init(gfx_ctx_vulkan_data_t *vk,
if (!GetInstanceProcAddr)
{
RARCH_ERR("[Vulkan] Failed to load vkGetInstanceProcAddr symbol, broken loader?\n");
#ifdef HAVE_VULKAN_CUSTOM_DRIVER
if (custom_driver)
return vulkan_context_init_system(vk, type);
#endif
return false;
}

Expand All @@ -5353,6 +5456,10 @@ bool vulkan_context_init(gfx_ctx_vulkan_data_t *vk,
if (!vulkan_symbol_wrapper_load_global_symbols())
{
RARCH_ERR("[Vulkan] Failed to load global Vulkan symbols, broken loader?\n");
#ifdef HAVE_VULKAN_CUSTOM_DRIVER
if (custom_driver)
return vulkan_context_init_system(vk, type);
#endif
return false;
}

Expand Down Expand Up @@ -5458,6 +5565,10 @@ bool vulkan_context_init(gfx_ctx_vulkan_data_t *vk,
if (vk->context.instance == VK_NULL_HANDLE)
{
RARCH_ERR("[Vulkan] Failed to create Vulkan instance.\n");
#ifdef HAVE_VULKAN_CUSTOM_DRIVER
if (custom_driver)
return vulkan_context_init_system(vk, type);
#endif
return false;
}
}
Expand Down Expand Up @@ -5492,9 +5603,18 @@ bool vulkan_context_init(gfx_ctx_vulkan_data_t *vk,
if (!vulkan_load_instance_symbols(vk))
{
RARCH_ERR("[Vulkan] Failed to load instance symbols.\n");
#ifdef HAVE_VULKAN_CUSTOM_DRIVER
if (custom_driver)
return vulkan_context_init_system(vk, type);
#endif
return false;
}

#ifdef HAVE_VULKAN_CUSTOM_DRIVER
if (custom_driver && !vulkan_context_custom_driver_ok(vk, app.apiVersion))
return vulkan_context_init_system(vk, type);
#endif

return true;
}

Expand Down Expand Up @@ -5581,6 +5701,8 @@ void vulkan_context_destroy(gfx_ctx_vulkan_data_t *vk,
if (record_android_capture_device_pending(vk->context.device))
video_st_flags &= ~VIDEO_FLAG_CACHE_CONTEXT;
#endif
if (vk->flags & VK_DATA_FLAG_DROP_CONTEXT)
video_st_flags &= ~VIDEO_FLAG_CACHE_CONTEXT;
if (video_st_flags & VIDEO_FLAG_CACHE_CONTEXT)
{
cached_device_vk = vk->context.device;
Expand Down Expand Up @@ -5653,7 +5775,14 @@ void vulkan_context_destroy(gfx_ctx_vulkan_data_t *vk,

if (vulkan_library)
{
#ifdef HAVE_VULKAN_CUSTOM_DRIVER
/* A linker namespace cannot be unloaded. */
if (!vulkan_library_custom)
dylib_close(vulkan_library);
vulkan_library_custom = false;
#else
dylib_close(vulkan_library);
#endif
vulkan_library = NULL;
}
}
Expand Down
Loading
Loading