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
19 changes: 17 additions & 2 deletions src/vulkan/wrapper/graphicsenv_hook.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<void *>(&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);
Expand Down
6 changes: 6 additions & 0 deletions src/vulkan/wrapper/wrapper_device.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
104 changes: 66 additions & 38 deletions src/vulkan/wrapper/wrapper_instance.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -84,17 +81,17 @@ bool has_intercepted_layer_paths = false;

#include <dlfcn.h>

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;

Expand All @@ -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");
}

}
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand All @@ -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
Expand All @@ -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;
Expand All @@ -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. */
}
}

Expand All @@ -401,15 +420,24 @@ 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);
vk_free2(vk_default_allocator(), pAllocator, instance);
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,
Expand All @@ -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);
Expand Down
57 changes: 37 additions & 20 deletions src/vulkan/wrapper/wrapper_log.c
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
#include <time.h>
#include <pthread.h>
#include <stdbool.h>

#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"
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
34 changes: 34 additions & 0 deletions src/vulkan/wrapper/wrapper_physical_device.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions src/vulkan/wrapper/wrapper_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading