diff --git a/src/nouveau/vulkan/layers/nvk_app_workarounds.c b/src/nouveau/vulkan/layers/nvk_app_workarounds.c new file mode 100644 index 00000000000..d67fb949e98 --- /dev/null +++ b/src/nouveau/vulkan/layers/nvk_app_workarounds.c @@ -0,0 +1,30 @@ +/* + * Copyright © 2026 Valve Corporation + * + * SPDX-License-Identifier: MIT + */ + +#include "vk_framebuffer.h" + +#include "nvk_cmd_buffer.h" +#include "nvk_device.h" +#include "nvk_entrypoints.h" +#include "nvk_image_view.h" + +#include "layers/nvk_app_workarounds.h" + +/* This layer implements various application WAs that don't need to be in the core driver. */ + +/* Metro Exodus */ +VKAPI_ATTR VkResult VKAPI_CALL +metro_exodus_GetSemaphoreCounterValue(VkDevice _device, VkSemaphore _semaphore, uint64_t *pValue) +{ + /* See https://gitlab.freedesktop.org/mesa/mesa/-/issues/5119. */ + if (_semaphore == VK_NULL_HANDLE) { + fprintf(stderr, "NVK: Ignoring vkGetSemaphoreCounterValue() with NULL semaphore (game bug)!\n"); + return VK_SUCCESS; + } + + VK_FROM_HANDLE(nvk_device, device, _device); + return device->layer_dispatch.app.GetSemaphoreCounterValue(_device, _semaphore, pValue); +} diff --git a/src/nouveau/vulkan/layers/nvk_app_workarounds.h b/src/nouveau/vulkan/layers/nvk_app_workarounds.h new file mode 100644 index 00000000000..4af18510346 --- /dev/null +++ b/src/nouveau/vulkan/layers/nvk_app_workarounds.h @@ -0,0 +1,15 @@ +/* + * Copyright © 2026 Valve Corporation + * + * SPDX-License-Identifier: MIT + */ + +#ifndef NVK_APP_WORKAROUNDS_H +#define NVK_APP_WORKAROUNDS_H + +#include + +VKAPI_ATTR VkResult VKAPI_CALL metro_exodus_GetSemaphoreCounterValue(VkDevice _device, VkSemaphore _semaphore, + uint64_t *pValue); + +#endif /* NVK_APP_WORKAROUNDS_H */ diff --git a/src/nouveau/vulkan/meson.build b/src/nouveau/vulkan/meson.build index 9e91c135da8..8c2a462615f 100644 --- a/src/nouveau/vulkan/meson.build +++ b/src/nouveau/vulkan/meson.build @@ -67,6 +67,8 @@ nvk_files = files( 'nvk_upload_queue.h', 'nvk_wsi.c', 'nvk_wsi.h', + 'layers/nvk_app_workarounds.c', + 'layers/nvk_app_workarounds.h', 'nvkmd/nouveau/nvkmd_nouveau.h', 'nvkmd/nouveau/nvkmd_nouveau_ctx.c', 'nvkmd/nouveau/nvkmd_nouveau_dev.c', diff --git a/src/nouveau/vulkan/nvk_device.c b/src/nouveau/vulkan/nvk_device.c index bcc59b18627..0514113d136 100644 --- a/src/nouveau/vulkan/nvk_device.c +++ b/src/nouveau/vulkan/nvk_device.c @@ -10,6 +10,7 @@ #include "nvk_physical_device.h" #include "nvk_sampler.h" #include "nvk_shader.h" +#include "layers/nvk_app_workarounds.h" #include "nvkmd/nvkmd.h" #include "vk_common_entrypoints.h" @@ -192,11 +193,31 @@ add_entrypoints(struct dispatch_table_builder *b, const struct vk_device_entrypo if (table < NVK_DISPATCH_TABLE_COUNT) b->used[table] = true; } + +static void +init_app_workarounds_entrypoints(struct nvk_device *device, struct dispatch_table_builder *b) +{ + const struct nvk_physical_device *pdev = nvk_device_physical(device); + const struct nvk_instance *instance = nvk_physical_device_instance(pdev); + struct vk_device_entrypoint_table table = {0}; + +#define SET_ENTRYPOINT(app_layer, entrypoint) table.entrypoint = app_layer##_##entrypoint; + if (!strcmp(instance->app_layer, "metroexodus")) { + SET_ENTRYPOINT(metro_exodus, GetSemaphoreCounterValue); + } +#undef SET_ENTRYPOINT + + add_entrypoints(b, &table, NVK_APP_DISPATCH_TABLE); +} + static void init_dispatch_tables(struct nvk_device *dev) { struct dispatch_table_builder b = {0}; b.tables[NVK_DEVICE_DISPATCH_TABLE] = &dev->vk.dispatch_table; + b.tables[NVK_APP_DISPATCH_TABLE] = &dev->layer_dispatch.app; + + init_app_workarounds_entrypoints(dev, &b); add_entrypoints(&b, &nvk_device_entrypoints, NVK_DISPATCH_TABLE_COUNT); add_entrypoints(&b, &wsi_device_entrypoints, NVK_DISPATCH_TABLE_COUNT); diff --git a/src/nouveau/vulkan/nvk_device.h b/src/nouveau/vulkan/nvk_device.h index 200363b95d5..5359e663adb 100644 --- a/src/nouveau/vulkan/nvk_device.h +++ b/src/nouveau/vulkan/nvk_device.h @@ -23,10 +23,12 @@ struct vk_pipeline_cache; enum nvk_dispatch_table { NVK_DEVICE_DISPATCH_TABLE, + NVK_APP_DISPATCH_TABLE, NVK_DISPATCH_TABLE_COUNT, }; struct nvk_layer_dispatch_tables { + struct vk_device_dispatch_table app; }; struct nvk_slm_area { diff --git a/src/nouveau/vulkan/nvk_instance.c b/src/nouveau/vulkan/nvk_instance.c index 730fd293470..ce62037edef 100644 --- a/src/nouveau/vulkan/nvk_instance.c +++ b/src/nouveau/vulkan/nvk_instance.c @@ -111,6 +111,7 @@ static const driOptionDescription nvk_dri_options[] = { DRI_CONF_VK_WSI_FORCE_SWAPCHAIN_TO_CURRENT_EXTENT(false) DRI_CONF_VK_X11_IGNORE_SUBOPTIMAL(false) DRI_CONF_VK_ZERO_VRAM(false) + DRI_CONF_NVK_APP_LAYER() DRI_CONF_SECTION_END }; @@ -127,6 +128,8 @@ nvk_init_dri_options(struct nvk_instance *instance) if (driQueryOptionb(&instance->dri_options, "vk_zero_vram")) instance->debug_flags |= NVK_DEBUG_ZERO_MEMORY; + + instance->app_layer = driQueryOptionstr(&instance->dri_options, "nvk_app_layer"); } VKAPI_ATTR VkResult VKAPI_CALL diff --git a/src/nouveau/vulkan/nvk_instance.h b/src/nouveau/vulkan/nvk_instance.h index a5d0cc23d7a..4e9ef5681fa 100644 --- a/src/nouveau/vulkan/nvk_instance.h +++ b/src/nouveau/vulkan/nvk_instance.h @@ -18,6 +18,7 @@ struct nvk_instance { struct driOptionCache dri_options; struct driOptionCache available_dri_options; + char *app_layer; uint8_t driver_build_sha[SHA1_DIGEST_LENGTH]; uint32_t force_vk_vendor; diff --git a/src/util/00-mesa-defaults.conf b/src/util/00-mesa-defaults.conf index e10a0d9d1cb..5da30ccdae1 100644 --- a/src/util/00-mesa-defaults.conf +++ b/src/util/00-mesa-defaults.conf @@ -1198,6 +1198,9 @@ TODO: document the other workarounds.