radv: rework app workarounds implemented using internal layers
Some checks are pending
macOS-CI / macOS-CI (dri) (push) Waiting to run
macOS-CI / macOS-CI (xlib) (push) Waiting to run

Just override the needed entrypoints.

Signed-off-by: Samuel Pitoiset <samuel.pitoiset@gmail.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/39549>
This commit is contained in:
Samuel Pitoiset 2026-01-27 08:14:41 +01:00 committed by Marge Bot
parent 875b6ab951
commit 83fabf7d41
9 changed files with 177 additions and 168 deletions

View file

@ -0,0 +1,123 @@
/*
* Copyright © 2026 Valve Corporation
*
* SPDX-License-Identifier: MIT
*/
#include "vk_framebuffer.h"
#include "radv_cmd_buffer.h"
#include "radv_device.h"
#include "radv_entrypoints.h"
#include "radv_image_view.h"
#include "layers/radv_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, "RADV: Ignoring vkGetSemaphoreCounterValue() with NULL semaphore (game bug)!\n");
return VK_SUCCESS;
}
VK_FROM_HANDLE(radv_device, device, _device);
return device->layer_dispatch.app.GetSemaphoreCounterValue(_device, _semaphore, pValue);
}
/* No Man's Sky */
VKAPI_ATTR VkResult VKAPI_CALL
no_mans_sky_CreateImageView(VkDevice _device, const VkImageViewCreateInfo *pCreateInfo,
const VkAllocationCallbacks *pAllocator, VkImageView *pView)
{
VK_FROM_HANDLE(radv_device, device, _device);
VkResult result;
result = device->layer_dispatch.app.CreateImageView(_device, pCreateInfo, pAllocator, pView);
if (result != VK_SUCCESS)
return result;
VK_FROM_HANDLE(radv_image_view, iview, *pView);
if ((iview->vk.aspects == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) &&
(iview->vk.usage &
(VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT))) {
/* No Man's Sky creates descriptors with depth/stencil aspects (only when Intel XESS is
* enabled apparently). and this is illegal in Vulkan. Ignore them by using NULL descriptors
* to workaroud GPU hangs.
*/
memset(&iview->descriptor, 0, sizeof(iview->descriptor));
}
return result;
}
/* Quantic Dream engine */
VKAPI_ATTR VkResult VKAPI_CALL
quantic_dream_UnmapMemory2(VkDevice _device, const VkMemoryUnmapInfo *pMemoryUnmapInfo)
{
/* Detroit: Become Human repeatedly calls vkMapMemory and vkUnmapMemory on the same buffer.
* This creates high overhead in the kernel due to mapping operation and page fault costs.
*
* Simply skip the unmap call to workaround it. Mapping an already-mapped region is UB in Vulkan,
* but will correctly return the mapped pointer on RADV.
*/
return VK_SUCCESS;
}
/* RAGE2 */
VKAPI_ATTR void VKAPI_CALL
rage2_CmdBeginRenderPass(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo *pRenderPassBegin,
VkSubpassContents contents)
{
VK_FROM_HANDLE(vk_framebuffer, framebuffer, pRenderPassBegin->framebuffer);
VK_FROM_HANDLE(radv_cmd_buffer, cmd_buffer, commandBuffer);
struct radv_device *device = radv_cmd_buffer_device(cmd_buffer);
VkRenderPassBeginInfo render_pass_begin = {
.sType = pRenderPassBegin->sType,
.pNext = pRenderPassBegin->pNext,
.renderPass = pRenderPassBegin->renderPass,
.framebuffer = pRenderPassBegin->framebuffer,
.clearValueCount = pRenderPassBegin->clearValueCount,
.pClearValues = pRenderPassBegin->pClearValues,
};
/* RAGE2 seems to incorrectly set the render area and with dynamic rendering the concept of
* framebuffer dimensions goes away. Forcing the render area to be the framebuffer dimensions
* restores previous logic and it fixes rendering issues.
*/
render_pass_begin.renderArea.offset.x = 0;
render_pass_begin.renderArea.offset.y = 0;
render_pass_begin.renderArea.extent.width = framebuffer->width;
render_pass_begin.renderArea.extent.height = framebuffer->height;
device->layer_dispatch.app.CmdBeginRenderPass(commandBuffer, &render_pass_begin, contents);
}
/* Strange Brigade (Vulkan) */
VKAPI_ATTR void VKAPI_CALL
strange_brigade_CmdPipelineBarrier2(VkCommandBuffer commandBuffer, const VkDependencyInfo *pDependencyInfo)
{
VK_FROM_HANDLE(radv_cmd_buffer, cmd_buffer, commandBuffer);
struct radv_device *device = radv_cmd_buffer_device(cmd_buffer);
for (uint32_t i = 0; i < pDependencyInfo->imageMemoryBarrierCount; i++) {
VkImageMemoryBarrier2 *barrier = (VkImageMemoryBarrier2 *)&pDependencyInfo->pImageMemoryBarriers[i];
if (barrier->newLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR &&
barrier->srcAccessMask == VK_ACCESS_COLOR_ATTACHMENT_READ_BIT) {
/* This game has a broken barrier right before present that causes rendering issues. Fix it
* by modifying the src access mask.
*/
barrier->srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
break;
}
}
device->layer_dispatch.app.CmdPipelineBarrier2(commandBuffer, pDependencyInfo);
}

View file

@ -0,0 +1,27 @@
/*
* Copyright © 2026 Valve Corporation
*
* SPDX-License-Identifier: MIT
*/
#ifndef RADV_APP_WORKAROUNDS_H
#define RADV_APP_WORKAROUNDS_H
#include <vulkan/vulkan.h>
VKAPI_ATTR VkResult VKAPI_CALL metro_exodus_GetSemaphoreCounterValue(VkDevice _device, VkSemaphore _semaphore,
uint64_t *pValue);
VKAPI_ATTR VkResult VKAPI_CALL no_mans_sky_CreateImageView(VkDevice _device, const VkImageViewCreateInfo *pCreateInfo,
const VkAllocationCallbacks *pAllocator, VkImageView *pView);
VKAPI_ATTR VkResult VKAPI_CALL quantic_dream_UnmapMemory2(VkDevice _device, const VkMemoryUnmapInfo *pMemoryUnmapInfo);
VKAPI_ATTR void VKAPI_CALL rage2_CmdBeginRenderPass(VkCommandBuffer commandBuffer,
const VkRenderPassBeginInfo *pRenderPassBegin,
VkSubpassContents contents);
VKAPI_ATTR void VKAPI_CALL strange_brigade_CmdPipelineBarrier2(VkCommandBuffer commandBuffer,
const VkDependencyInfo *pDependencyInfo);
#endif /* RADV_APP_WORKAROUNDS_H */

View file

@ -1,21 +0,0 @@
/*
* Copyright © 2021 Valve Corporation
*
* SPDX-License-Identifier: MIT
*/
#include "radv_device.h"
#include "radv_entrypoints.h"
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, "RADV: Ignoring vkGetSemaphoreCounterValue() with NULL semaphore (game bug)!\n");
return VK_SUCCESS;
}
VK_FROM_HANDLE(radv_device, device, _device);
return device->layer_dispatch.app.GetSemaphoreCounterValue(_device, _semaphore, pValue);
}

View file

@ -1,35 +0,0 @@
/*
* Copyright © 2025 Valve Corporation
*
* SPDX-License-Identifier: MIT
*/
#include "radv_device.h"
#include "radv_entrypoints.h"
#include "radv_image_view.h"
VKAPI_ATTR VkResult VKAPI_CALL
no_mans_sky_CreateImageView(VkDevice _device, const VkImageViewCreateInfo *pCreateInfo,
const VkAllocationCallbacks *pAllocator, VkImageView *pView)
{
VK_FROM_HANDLE(radv_device, device, _device);
VkResult result;
result = device->layer_dispatch.app.CreateImageView(_device, pCreateInfo, pAllocator, pView);
if (result != VK_SUCCESS)
return result;
VK_FROM_HANDLE(radv_image_view, iview, *pView);
if ((iview->vk.aspects == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) &&
(iview->vk.usage &
(VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT))) {
/* No Man's Sky creates descriptors with depth/stencil aspects (only when Intel XESS is
* enabled apparently). and this is illegal in Vulkan. Ignore them by using NULL descriptors
* to workaroud GPU hangs.
*/
memset(&iview->descriptor, 0, sizeof(iview->descriptor));
}
return result;
}

View file

@ -1,19 +0,0 @@
/*
* Copyright © 2024 Valve Corporation
*
* SPDX-License-Identifier: MIT
*/
#include "radv_entrypoints.h"
VKAPI_ATTR VkResult VKAPI_CALL
quantic_dream_UnmapMemory2(VkDevice _device, const VkMemoryUnmapInfo *pMemoryUnmapInfo)
{
/* Detroit: Become Human repeatedly calls vkMapMemory and vkUnmapMemory on the same buffer.
* This creates high overhead in the kernel due to mapping operation and page fault costs.
*
* Simply skip the unmap call to workaround it. Mapping an already-mapped region is UB in Vulkan,
* but will correctly return the mapped pointer on RADV.
*/
return VK_SUCCESS;
}

View file

@ -1,39 +0,0 @@
/*
* Copyright © 2023 Valve Corporation
*
* SPDX-License-Identifier: MIT
*/
#include "radv_cmd_buffer.h"
#include "radv_device.h"
#include "radv_entrypoints.h"
#include "vk_framebuffer.h"
VKAPI_ATTR void VKAPI_CALL
rage2_CmdBeginRenderPass(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo *pRenderPassBegin,
VkSubpassContents contents)
{
VK_FROM_HANDLE(vk_framebuffer, framebuffer, pRenderPassBegin->framebuffer);
VK_FROM_HANDLE(radv_cmd_buffer, cmd_buffer, commandBuffer);
struct radv_device *device = radv_cmd_buffer_device(cmd_buffer);
VkRenderPassBeginInfo render_pass_begin = {
.sType = pRenderPassBegin->sType,
.pNext = pRenderPassBegin->pNext,
.renderPass = pRenderPassBegin->renderPass,
.framebuffer = pRenderPassBegin->framebuffer,
.clearValueCount = pRenderPassBegin->clearValueCount,
.pClearValues = pRenderPassBegin->pClearValues,
};
/* RAGE2 seems to incorrectly set the render area and with dynamic rendering the concept of
* framebuffer dimensions goes away. Forcing the render area to be the framebuffer dimensions
* restores previous logic and it fixes rendering issues.
*/
render_pass_begin.renderArea.offset.x = 0;
render_pass_begin.renderArea.offset.y = 0;
render_pass_begin.renderArea.extent.width = framebuffer->width;
render_pass_begin.renderArea.extent.height = framebuffer->height;
device->layer_dispatch.app.CmdBeginRenderPass(commandBuffer, &render_pass_begin, contents);
}

View file

@ -1,31 +0,0 @@
/*
* Copyright © 2026 Valve Corporation
*
* SPDX-License-Identifier: MIT
*/
#include "radv_cmd_buffer.h"
#include "radv_device.h"
#include "radv_entrypoints.h"
VKAPI_ATTR void VKAPI_CALL
strange_brigade_CmdPipelineBarrier2(VkCommandBuffer commandBuffer, const VkDependencyInfo *pDependencyInfo)
{
VK_FROM_HANDLE(radv_cmd_buffer, cmd_buffer, commandBuffer);
struct radv_device *device = radv_cmd_buffer_device(cmd_buffer);
for (uint32_t i = 0; i < pDependencyInfo->imageMemoryBarrierCount; i++) {
VkImageMemoryBarrier2 *barrier = (VkImageMemoryBarrier2 *)&pDependencyInfo->pImageMemoryBarriers[i];
if (barrier->newLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR &&
barrier->srcAccessMask == VK_ACCESS_COLOR_ATTACHMENT_READ_BIT) {
/* This game has a broken barrier right before present that causes rendering issues. Fix it
* by modifying the src access mask.
*/
barrier->srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
break;
}
}
device->layer_dispatch.app.CmdPipelineBarrier2(commandBuffer, pDependencyInfo);
}

View file

@ -17,13 +17,6 @@ radv_entrypoints_gen_command += [
'--device-prefix', 'rmv',
'--device-prefix', 'ctx_roll',
# Application layer entrypoints
'--device-prefix', 'metro_exodus',
'--device-prefix', 'rage2',
'--device-prefix', 'quantic_dream',
'--device-prefix', 'no_mans_sky',
'--device-prefix', 'strange_brigade',
# Command buffer annotation layer entrypoints
'--device-prefix', 'annotate',
]
@ -38,12 +31,8 @@ radv_entrypoints = custom_target(
libradv_files = files(
'bvh/bvh.h',
'layers/radv_app_workarounds.c',
'layers/radv_ctx_roll_layer.c',
'layers/radv_metro_exodus.c',
'layers/radv_rage2.c',
'layers/radv_quantic_dream.c',
'layers/radv_no_mans_sky.c',
'layers/radv_strange_brigade.c',
'layers/radv_rmv_layer.c',
'layers/radv_rra_layer.c',
'layers/radv_sqtt_layer.c',

View file

@ -26,6 +26,7 @@
#include <sys/inotify.h>
#endif
#include "layers/radv_app_workarounds.h"
#include "meta/radv_meta.h"
#include "util/disk_cache.h"
#include "util/u_debug.h"
@ -761,6 +762,30 @@ add_entrypoints(struct dispatch_table_builder *b, const struct vk_device_entrypo
b->used[table] = true;
}
static void
init_app_workarounds_entrypoints(struct radv_device *device, struct dispatch_table_builder *b)
{
const struct radv_physical_device *pdev = radv_device_physical(device);
const struct radv_instance *instance = radv_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->drirc.debug.app_layer, "metroexodus")) {
SET_ENTRYPOINT(metro_exodus, GetSemaphoreCounterValue);
} else if (!strcmp(instance->drirc.debug.app_layer, "rage2")) {
SET_ENTRYPOINT(rage2, CmdBeginRenderPass);
} else if (!strcmp(instance->drirc.debug.app_layer, "quanticdream")) {
SET_ENTRYPOINT(quantic_dream, UnmapMemory2);
} else if (!strcmp(instance->drirc.debug.app_layer, "no_mans_sky")) {
SET_ENTRYPOINT(no_mans_sky, CreateImageView);
} else if (!strcmp(instance->drirc.debug.app_layer, "strange_brigade")) {
SET_ENTRYPOINT(strange_brigade, CmdPipelineBarrier2);
}
#undef SET_ENTRYPOINT
add_entrypoints(b, &table, RADV_APP_DISPATCH_TABLE);
}
static void
init_dispatch_tables(struct radv_device *device, struct radv_physical_device *pdev)
{
@ -778,17 +803,7 @@ init_dispatch_tables(struct radv_device *device, struct radv_physical_device *pd
if (radv_device_fault_detection_enabled(device) || gather_ctx_rolls)
add_entrypoints(&b, &annotate_device_entrypoints, RADV_ANNOTATE_DISPATCH_TABLE);
if (!strcmp(instance->drirc.debug.app_layer, "metroexodus")) {
add_entrypoints(&b, &metro_exodus_device_entrypoints, RADV_APP_DISPATCH_TABLE);
} else if (!strcmp(instance->drirc.debug.app_layer, "rage2")) {
add_entrypoints(&b, &rage2_device_entrypoints, RADV_APP_DISPATCH_TABLE);
} else if (!strcmp(instance->drirc.debug.app_layer, "quanticdream")) {
add_entrypoints(&b, &quantic_dream_device_entrypoints, RADV_APP_DISPATCH_TABLE);
} else if (!strcmp(instance->drirc.debug.app_layer, "no_mans_sky")) {
add_entrypoints(&b, &no_mans_sky_device_entrypoints, RADV_APP_DISPATCH_TABLE);
} else if (!strcmp(instance->drirc.debug.app_layer, "strange_brigade")) {
add_entrypoints(&b, &strange_brigade_device_entrypoints, RADV_APP_DISPATCH_TABLE);
}
init_app_workarounds_entrypoints(device, &b);
if (instance->vk.trace_mode & RADV_TRACE_MODE_RGP)
add_entrypoints(&b, &sqtt_device_entrypoints, RADV_RGP_DISPATCH_TABLE);