diff --git a/src/vulkan/wrapper/graphicsenv_hook.cpp b/src/vulkan/wrapper/graphicsenv_hook.cpp index 776f93b5359..a0f1b85db50 100644 --- a/src/vulkan/wrapper/graphicsenv_hook.cpp +++ b/src/vulkan/wrapper/graphicsenv_hook.cpp @@ -56,8 +56,23 @@ bool set_layer_paths() { const char *env = getenv("WRAPPER_LAYER_PATH"); if (env) env_layers_path = env; - if (env_layers_path.empty()) - env_layers_path = WRAPPER_LAYERS_PATH; + if (env_layers_path.empty()) { + /* Keep app-provided paths and search beside this installed driver; + * the legacy fallback alone points only at unrelated applications. */ + env_layers_path = getLayerPaths(instance); + Dl_info module = {}; + if (dladdr(reinterpret_cast(&set_layer_paths), &module) && module.dli_fname) { + std::string path(module.dli_fname); + auto slash = path.find_last_of('/'); + if (slash != std::string::npos) { + if (!env_layers_path.empty()) + env_layers_path += ':'; + env_layers_path += path.substr(0, slash); + } + } + if (env_layers_path.empty()) + env_layers_path = WRAPPER_LAYERS_PATH; + } } setLayerPaths(instance, app_namespace, env_layers_path); diff --git a/src/vulkan/wrapper/wrapper_device.c b/src/vulkan/wrapper/wrapper_device.c index 2602532d383..9afa8fc7529 100644 --- a/src/vulkan/wrapper/wrapper_device.c +++ b/src/vulkan/wrapper/wrapper_device.c @@ -1230,6 +1230,12 @@ wrapper_emit_diag(struct wrapper_physical_device *pdev, D("engine: '%s' -> detected %s (app '%s')\n", eng, detected, pdev->instance->vk.app_info.app_name ? pdev->instance->vk.app_info.app_name : ""); D("--- native capabilities (what THIS device actually has) ---\n"); + D(" fillModeNonSolid : native=%d advertised=%d\n", + pdev->base_supported_features.fillModeNonSolid, + pdev->vk.supported_features.fillModeNonSolid); + D(" shaderClipDistance : native=%d advertised=%d\n", + pdev->base_supported_features.shaderClipDistance, + pdev->vk.supported_features.shaderClipDistance); D(" textureCompressionBC : %d\n", pdev->base_supported_features.textureCompressionBC); D(" textureCompressionASTC_LDR : %d\n", pdev->base_supported_features.textureCompressionASTC_LDR); D(" robustBufferAccess2 : %d\n", pdev->base_supported_features.robustBufferAccess2); diff --git a/src/vulkan/wrapper/wrapper_instance.c b/src/vulkan/wrapper/wrapper_instance.c index ae7311c1b36..586f66f57d1 100644 --- a/src/vulkan/wrapper/wrapper_instance.c +++ b/src/vulkan/wrapper/wrapper_instance.c @@ -69,9 +69,6 @@ static PFN_vkGetInstanceProcAddr get_instance_proc_addr; static PFN_vkEnumerateInstanceVersion enumerate_instance_version; static PFN_vkEnumerateInstanceExtensionProperties enumerate_instance_extension_properties; static PFN_vkEnumerateInstanceLayerProperties enumerate_instance_layer_properties; -static PFN_vkCreateDebugUtilsMessengerEXT create_debug_utils_messenger; -static PFN_vkDestroyDebugUtilsMessengerEXT destroy_debug_utils_messenger; -VkDebugUtilsMessengerEXT debugUtilsMessenger; static struct vk_instance_extension_table *supported_instance_extensions; bool has_intercepted_layer_paths = false; @@ -84,17 +81,17 @@ bool has_intercepted_layer_paths = false; #include -static void init_debug_messenger(VkInstance instance) +static void init_debug_messenger(struct wrapper_instance *wrapper) { - create_debug_utils_messenger = (PFN_vkCreateDebugUtilsMessengerEXT)get_instance_proc_addr(instance, "vkCreateDebugUtilsMessengerEXT"); - destroy_debug_utils_messenger = (PFN_vkDestroyDebugUtilsMessengerEXT)get_instance_proc_addr(instance, "vkDestroyDebugUtilsMessengerEXT"); + VkInstance instance = wrapper->dispatch_handle; + PFN_vkCreateDebugUtilsMessengerEXT create_debug_utils_messenger = + (PFN_vkCreateDebugUtilsMessengerEXT)get_instance_proc_addr(instance, "vkCreateDebugUtilsMessengerEXT"); + wrapper->destroy_debug_messenger = (PFN_vkDestroyDebugUtilsMessengerEXT)get_instance_proc_addr(instance, "vkDestroyDebugUtilsMessengerEXT"); - if (create_debug_utils_messenger && destroy_debug_utils_messenger) { + if (create_debug_utils_messenger && wrapper->destroy_debug_messenger) { WRAPPER_LOG(info, "Creating debug messenger"); VkDebugUtilsMessengerCreateInfoEXT messengerInfo; const VkDebugUtilsMessageSeverityFlagsEXT kSeveritiesToLog = - VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; @@ -111,7 +108,11 @@ static void init_debug_messenger(VkInstance instance) messengerInfo.pfnUserCallback = &wrapper_debug_utils_messenger; messengerInfo.pUserData = NULL; - create_debug_utils_messenger(instance, &messengerInfo, NULL, &debugUtilsMessenger); + VkResult result = create_debug_utils_messenger(instance, &messengerInfo, NULL, &wrapper->debug_messenger); + wrapper_diag_append("[WRAPPER_VALIDATION] debug messenger result=%d; %s\n", + result, result == VK_SUCCESS ? "capture active" : "capture unavailable"); + } else { + wrapper_diag_append("[WRAPPER_VALIDATION] debug messenger entrypoints unavailable; no validation capture\n"); } } @@ -263,6 +264,21 @@ set_wrapper_required_extensions(const struct vk_instance *instance, *enable_extension_count = count; } +static bool +has_debug_utils(const char *layer) +{ + VkExtensionProperties props[VK_INSTANCE_EXTENSION_COUNT]; + uint32_t count = ARRAY_SIZE(props); + VkResult result = enumerate_instance_extension_properties(layer, &count, props); + if (result != VK_SUCCESS && result != VK_INCOMPLETE) + return false; + for (uint32_t i = 0; i < MIN2(count, ARRAY_SIZE(props)); i++) { + if (!strcmp(props[i].extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) + return true; + } + return false; +} + VKAPI_ATTR VkResult VKAPI_CALL wrapper_CreateInstance(const VkInstanceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, @@ -328,20 +344,25 @@ wrapper_CreateInstance(const VkInstanceCreateInfo *pCreateInfo, } wrapper_create_info.pApplicationInfo = &wrapper_application_info; + + bool validation_enabled = false; + const char *enabled_layers[2]; + const void *original_next = wrapper_create_info.pNext; + uint32_t original_extension_count = wrapper_enable_extension_count; /* Load whichever debug layers were requested via WRAPPER_LOG_LEVEL: * "validation" -> VK_LAYER_KHRONOS_validation (API-usage errors), * "apidump" -> VK_LAYER_LUNARG_api_dump (every Vulkan call, very spammy). * Both can be enabled together. */ if (WRAPPER_LOG_LEVEL(validation) || WRAPPER_LOG_LEVEL(apidump)) { - /* Enumerate available layers (only if the layer-path jailbreak worked). */ + /* Existing loader paths may work even when path interception is unavailable. */ bool have_validation = false, have_api_dump = false; - if (has_intercepted_layer_paths) { - uint32_t layer_count = 0; - enumerate_instance_layer_properties(&layer_count, NULL); - if (layer_count) { - VkLayerProperties layer_props[layer_count]; - enumerate_instance_layer_properties(&layer_count, layer_props); + if (enumerate_instance_layer_properties) { + VkLayerProperties layer_props[64]; + uint32_t layer_count = ARRAY_SIZE(layer_props); + VkResult layer_result = enumerate_instance_layer_properties(&layer_count, layer_props); + if (layer_result == VK_SUCCESS || layer_result == VK_INCOMPLETE) { + layer_count = MIN2(layer_count, ARRAY_SIZE(layer_props)); for (uint32_t i = 0; i < layer_count; i++) { if (!strcmp(layer_props[i].layerName, WRAPPER_VALIDATION_LAYER)) have_validation = true; @@ -353,6 +374,19 @@ wrapper_CreateInstance(const VkInstanceCreateInfo *pCreateInfo, bool enable_validation = WRAPPER_LOG_LEVEL(validation) && have_validation; bool enable_api_dump = WRAPPER_LOG_LEVEL(apidump) && have_api_dump; + bool debug_utils = enable_validation && + (has_debug_utils(NULL) || has_debug_utils(WRAPPER_VALIDATION_LAYER)); + enable_validation &= debug_utils; + validation_enabled = enable_validation; + wrapper_diag_append("[WRAPPER_VALIDATION] layer=%s paths=%s debug_utils=%s; validation %s\n", + have_validation ? "found" : "missing", + has_intercepted_layer_paths ? "configured" : "unchanged", + debug_utils ? "available" : "unavailable", + enable_validation ? "requested" : "unavailable (not a clean validation result)"); + if (enable_validation) { + assert(wrapper_enable_extension_count < VK_INSTANCE_EXTENSION_COUNT); + wrapper_enable_extensions[wrapper_enable_extension_count++] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME; + } /* If a requested layer can't be set up, warn and continue WITHOUT it -- * a game that runs without validation beats one that won't start. Print @@ -366,10 +400,6 @@ wrapper_CreateInstance(const VkInstanceCreateInfo *pCreateInfo, fprintf(stderr, "[wrapper] api_dump layer requested but unavailable; " "continuing without it\n"); - /* The enabled list and settings are read by create_instance() below, - * which is outside this block -- give them static lifetime. The layer - * name string literals already have static storage. */ - static const char *enabled_layers[2]; uint32_t enabled_layer_count = 0; if (enable_validation) enabled_layers[enabled_layer_count++] = WRAPPER_VALIDATION_LAYER; @@ -380,19 +410,8 @@ wrapper_CreateInstance(const VkInstanceCreateInfo *pCreateInfo, wrapper_create_info.enabledLayerCount = enabled_layer_count; wrapper_create_info.ppEnabledLayerNames = enabled_layers; - /* Best-practices settings only apply to the validation layer. */ - if (enable_validation) { - static const VkBool32 settings_validate_best_practices = VK_TRUE; - static const VkLayerSettingEXT layer_settings[] = { - {WRAPPER_VALIDATION_LAYER, "validate_best_practices_arm", VK_LAYER_SETTING_TYPE_BOOL32_EXT, 1, &settings_validate_best_practices}, - {WRAPPER_VALIDATION_LAYER, "validate_best_practices", VK_LAYER_SETTING_TYPE_BOOL32_EXT, 1, &settings_validate_best_practices}, - }; - static VkLayerSettingsCreateInfoEXT layer_settings_info = { - VK_STRUCTURE_TYPE_LAYER_SETTINGS_CREATE_INFO_EXT, NULL, 2, layer_settings, - }; - layer_settings_info.pNext = wrapper_create_info.pNext; - wrapper_create_info.pNext = &layer_settings_info; - } + /* Core API validation only: best-practice performance advice would + * consume the bounded report without identifying invalid operations. */ } } @@ -401,6 +420,15 @@ wrapper_CreateInstance(const VkInstanceCreateInfo *pCreateInfo, result = create_instance(&wrapper_create_info, pAllocator, &instance->dispatch_handle); + if (result != VK_SUCCESS && validation_enabled) { + wrapper_diag_append("[WRAPPER_VALIDATION] validated instance failed result=%d; retrying without diagnostic layers\n", result); + wrapper_create_info.enabledLayerCount = pCreateInfo->enabledLayerCount; + wrapper_create_info.ppEnabledLayerNames = pCreateInfo->ppEnabledLayerNames; + wrapper_create_info.enabledExtensionCount = original_extension_count; + wrapper_create_info.pNext = original_next; + validation_enabled = false; + result = create_instance(&wrapper_create_info, pAllocator, &instance->dispatch_handle); + } if (result != VK_SUCCESS) { WRAPPER_LOG(error, "Failed driver createInstance, res %d", result); vk_instance_finish(&instance->vk); @@ -408,8 +436,8 @@ wrapper_CreateInstance(const VkInstanceCreateInfo *pCreateInfo, return vk_error(NULL, result); } - if (WRAPPER_LOG_LEVEL(validation)) { - init_debug_messenger(instance->dispatch_handle); + if (validation_enabled) { + init_debug_messenger(instance); } vk_instance_dispatch_table_load(&instance->dispatch_table, @@ -427,8 +455,8 @@ wrapper_DestroyInstance(VkInstance _instance, { VK_FROM_HANDLE(wrapper_instance, instance, _instance); - if (destroy_debug_utils_messenger) - destroy_debug_utils_messenger(instance->dispatch_handle, debugUtilsMessenger, pAllocator); + if (instance->destroy_debug_messenger && instance->debug_messenger) + instance->destroy_debug_messenger(instance->dispatch_handle, instance->debug_messenger, NULL); instance->dispatch_table.DestroyInstance(instance->dispatch_handle, pAllocator); diff --git a/src/vulkan/wrapper/wrapper_log.c b/src/vulkan/wrapper/wrapper_log.c index a2af67a5a2f..0d17cee3b39 100644 --- a/src/vulkan/wrapper/wrapper_log.c +++ b/src/vulkan/wrapper/wrapper_log.c @@ -1,8 +1,12 @@ #include +#include +#include #include "wrapper_log.h" #include "wrapper_util.h" +void wrapper_diag_append(const char *fmt, ...); + #define PATH_MAX_SIZE 1024 #define WRAPPER_DIR "/sdcard/wrapper" #define WRAPPER_SHADER_LOG_PATH WRAPPER_DIR "/shaders" @@ -136,8 +140,16 @@ void write_to_logfile(const char *fmt, const char *level, ...) { void init_wrapper_logging() { + /* Diagnostic package: keep the existing capability/format/copy probes on + * even when the launcher does not pass the guest diagnostic environment + * through to the native OpenGL/Zink driver process. */ + setenv("WRAPPER_DIAG", "1", 1); + fprintf(stderr, "[WRAPPER_DIAG] isaac-wrapper-validation-3 loaded; validation status and bounded messages captured automatically\n"); + fflush(stderr); + char *wrapper_log_level_env = getenv("WRAPPER_LOG_LEVEL"); parse_wrapper_debug_str(wrapper_log_level_env); + wrapper_log_mask |= WRAPPER_LOG_VALIDATION; CREATE_FOLDER(WRAPPER_DIR, 0770); CREATE_FOLDER(WRAPPER_LOG_PATH, 0770); @@ -166,25 +178,30 @@ wrapper_debug_utils_messenger(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeve const VkDebugUtilsMessengerCallbackDataEXT *callbackData, void *userData) { - static FILE *vvl_log_file = NULL; - const char* messageIdName = callbackData->pMessageIdName; - int32_t messageIdNumber = callbackData->messageIdNumber; - const char* message = callbackData->pMessage; - - if (!vvl_log_file) { - char date[256]; - char *vvl_log_filename; - - get_formatted_date_time(date, 256); - - asprintf(&vvl_log_filename, "%s/%s_%s", WRAPPER_VALIDATION_LOG_PATH, get_executable_name(), date); - vvl_log_file = fopen(vvl_log_filename, "w"); + /* The app collects the shared diagnostic stream, not /sdcard/wrapper/validation. + * Bound and deduplicate warning/error messages so a per-draw error cannot + * drown out the report or stall the game. Keep the VUID and operation text. */ + static pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER; + static int32_t seen_ids[64]; + static unsigned seen_count; + if (callbackData && + (messageSeverity & (VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT))) { + pthread_mutex_lock(&log_mutex); + bool seen = false; + for (unsigned i = 0; i < seen_count; i++) + seen |= seen_ids[i] == callbackData->messageIdNumber; + if (!seen && seen_count < 64) { + seen_ids[seen_count++] = callbackData->messageIdNumber; + wrapper_diag_append("[WRAPPER_VALIDATION] severity=0x%x type=0x%x id=%.256s code=%d: %.8192s\n", + messageSeverity, messageTypes, + callbackData->pMessageIdName ? callbackData->pMessageIdName : "unknown", + callbackData->messageIdNumber, + callbackData->pMessage ? callbackData->pMessage : "no text"); + if (seen_count == 64) + wrapper_diag_append("[WRAPPER_VALIDATION] unique-message limit reached; further messages suppressed\n"); + } + pthread_mutex_unlock(&log_mutex); } - - if (vvl_log_file) { - fprintf(vvl_log_file, "[%s] Code %i : %s\n", messageIdName, messageIdNumber, message); - fflush(vvl_log_file); - } - - return 0; + return VK_FALSE; } diff --git a/src/vulkan/wrapper/wrapper_physical_device.c b/src/vulkan/wrapper/wrapper_physical_device.c index 3b1d7c34a11..f2ddc5ab9df 100644 --- a/src/vulkan/wrapper/wrapper_physical_device.c +++ b/src/vulkan/wrapper/wrapper_physical_device.c @@ -16,6 +16,27 @@ #include "util/os_misc.h" #include "util/u_debug.h" +static bool +wrapper_zink_discard_workaround(const struct wrapper_physical_device *pdevice) +{ + const char *engine = pdevice->instance->vk.app_info.engine_name; + return engine && !strcmp(engine, "mesa zink") && + pdevice->driver_properties.driverID == VK_DRIVER_ID_ARM_PROPRIETARY && + pdevice->properties2.properties.deviceID == 0xd8300010; +} + +static void +wrapper_zink_discard_cache_uuid(const struct wrapper_physical_device *pdevice, + VkPhysicalDeviceProperties *properties) +{ + /* Zink keys both its NIR/SPIR-V and pipeline disk caches with this UUID. + * Separate the discard variant from shaders compiled with demote enabled. + * Native pipeline cache blobs still pass through to the same base driver. + */ + if (wrapper_zink_discard_workaround(pdevice)) + properties->pipelineCacheUUID[0] ^= 0xd1; +} + static uint32_t parse_vk_version_from_env_named(const char *name) { @@ -438,6 +459,17 @@ VkResult enumerate_physical_device(struct vk_instance *_instance) const uint32_t engine_version = instance->vk.app_info.engine_version; const uint32_t driver_version = pdevice->properties2.properties.driverVersion; + /* Comparison build for RGB flicker with Zink fragment discard on G925. + * Mask the feature as well as the extension: it is promoted in Vulkan + * 1.3, so hiding only the extension can leave the core path available. + * Keep base capabilities intact; Zink generates the fallback itself. + */ + if (wrapper_zink_discard_workaround(pdevice)) { + pdevice->vk.supported_extensions.EXT_shader_demote_to_helper_invocation = false; + supported_features->shaderDemoteToHelperInvocation = false; + WRAPPER_LOG(info, "Zink discard comparison: demote extension and core feature hidden; separate shader cache"); + } + /* DXVK forces hostQueryReset on from D3D_FEATURE_LEVEL_9_1 up, so a driver * without it gets no D3D11 device at any level. Gated on the driver * rather than on the engine name: the base-lacks guard already limits @@ -767,6 +799,7 @@ wrapper_GetPhysicalDeviceProperties(VkPhysicalDevice physicalDevice, VK_FROM_HANDLE(wrapper_physical_device, pdevice, physicalDevice); pdevice->dispatch_table.GetPhysicalDeviceProperties( pdevice->dispatch_handle, pProperties); + wrapper_zink_discard_cache_uuid(pdevice, pProperties); uint32_t api_version = wrapper_effective_api_version(pdevice, pProperties->apiVersion); @@ -850,6 +883,7 @@ wrapper_GetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevice, VK_FROM_HANDLE(wrapper_physical_device, pdevice, physicalDevice); pdevice->dispatch_table.GetPhysicalDeviceProperties2( pdevice->dispatch_handle, pProperties); + wrapper_zink_discard_cache_uuid(pdevice, &pProperties->properties); uint32_t api_version = wrapper_effective_api_version( pdevice, pProperties->properties.apiVersion); diff --git a/src/vulkan/wrapper/wrapper_private.h b/src/vulkan/wrapper/wrapper_private.h index d87b019ba39..6940d7064ae 100644 --- a/src/vulkan/wrapper/wrapper_private.h +++ b/src/vulkan/wrapper/wrapper_private.h @@ -28,6 +28,8 @@ struct wrapper_instance { VkInstance dispatch_handle; struct vk_instance_dispatch_table dispatch_table; + VkDebugUtilsMessengerEXT debug_messenger; + PFN_vkDestroyDebugUtilsMessengerEXT destroy_debug_messenger; }; VK_DEFINE_HANDLE_CASTS(wrapper_instance, vk.base, VkInstance,