nvk: add app workaround layer
Some checks are pending
macOS-CI / macOS-CI (dri) (push) Waiting to run
macOS-CI / macOS-CI (xlib) (push) Waiting to run

This adopts the device internal app workaround layer from radv

The layer allows to fix up game input in the layer instead of
adding workarounds within the driver.

Initially this only includes the workaround for Metro exodus as
I have verified that it fixes a crash on NVK. Follow up commits
can add the other relevant workarounds when the fixes are verified
to be needed for NVK.

Reviewed-by: Mary Guillemard <mary@mary.zone>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/39870>
This commit is contained in:
Thomas H.P. Andersen 2026-02-12 13:01:35 +01:00 committed by Marge Bot
parent 0a6509e94c
commit 331af5e746
9 changed files with 83 additions and 0 deletions

View file

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

View file

@ -0,0 +1,15 @@
/*
* Copyright © 2026 Valve Corporation
*
* SPDX-License-Identifier: MIT
*/
#ifndef NVK_APP_WORKAROUNDS_H
#define NVK_APP_WORKAROUNDS_H
#include <vulkan/vulkan.h>
VKAPI_ATTR VkResult VKAPI_CALL metro_exodus_GetSemaphoreCounterValue(VkDevice _device, VkSemaphore _semaphore,
uint64_t *pValue);
#endif /* NVK_APP_WORKAROUNDS_H */

View file

@ -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',

View file

@ -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);

View file

@ -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 {

View file

@ -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

View file

@ -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;

View file

@ -1198,6 +1198,9 @@ TODO: document the other workarounds.
<!-- Tries to use NvAPI_D3D11_CreateCubinComputeShaderWithName otherwise -->
<option name="force_vk_vendor" value="-1" />
</application>
<application name="Metro Exodus (Linux native)" application_name_match="metroexodus">
<option name="nvk_app_layer" value="metroexodus" />
</application>
<application name="X4 Foundations" executable="X4">
<option name="force_vk_vendor" value="-1" />
</application>

View file

@ -974,4 +974,10 @@
#define DRI_CONF_DZN_DISABLE(def) \
DRI_CONF_OPT_B(dzn_disable, def, "Fail instance creation")
/**
* \brief NVK specific configuration options
*/
#define DRI_CONF_NVK_APP_LAYER() DRI_CONF_OPT_S_NODEF(nvk_app_layer, "Select an application layer.")
#endif