HansKristian-Work/vkd3d-proton
GitHub: HansKristian-Work/vkd3d-proton
基于 Vulkan 实现 Direct3D 12 API 的翻译层,为 Proton 提供高性能的 Windows 游戏兼容支持。
Stars: 2709 | Forks: 278
# vkd3d-proton
vkd3d-proton 是 VKD3D 的一个分支,旨在基于 Vulkan 实现完整的 Direct3D 12 API。
该项目为 [Proton](https://github.com/ValveSoftware/Proton) 中的 Direct3D 12 支持提供开发支持。
## 上游
原始项目可在 [WineHQ](https://gitlab.winehq.org/wine/vkd3d) 获取。
## 优先级
性能和游戏兼容性是我们的重要目标,为此甚至不惜牺牲对旧版驱动和系统的兼容性。
为了提升游戏性能和兼容性,我们积极地利用了现代 Vulkan 扩展和特性。
建议您尽可能使用能够获取的最新驱动,以获得最佳体验。
向后兼容 vkd3d 独立 API 并非本项目的目标。
## 驱动
为了能以合理的性能实现 D3D12,对驱动有一些硬性要求。
- Vulkan 1.3
- 描述符索引,且除了 UniformBuffer 之外的所有类型,至少具有 1000000 个 UpdateAfterBind 描述符。
基本上必须支持 `VkPhysicalDeviceDescriptorIndexingFeatures` 中的所有特性。
- 此外,还需要以下设备特性:
- `samplerMirrorClampToEdge`
- `shaderDrawParameters`
- `VK_EXT_robustness2`
- `VK_KHR_push_descriptor`
为了获得最优或正确的行为,一些值得注意的扩展**应该**得到支持。
这些扩展以后很可能会变为强制要求。
- `VK_EXT_image_view_min_lod`
同样强烈建议支持 `VK_EXT_mutable_descriptor_type`(或供应商 `VALVE` 别名)和 `VK_EXT_descriptor_buffer`,但并非强制要求。
### AMD (RADV)
对于 AMD,RADV 是推荐的驱动程序,也是在 AMD GPU 上进行最多测试的驱动。
目前的最低要求是 Mesa 22.0。
注意:对于较旧的 Mesa 版本,请使用 v2.6 版本。
### NVIDIA
[Vulkan beta 驱动](https://developer.nvidia.com/vulkan-driver) 通常包含我们在让游戏运行过程中发现的最新驱动修复。
始终首选最新的驱动程序(稳定版、beta 版或 Vulkan beta 轨道)。
如果您遇到问题,请始终尝试最新的驱动。
至少需要 535 系列驱动,该系列修复了大量 bug。
### Intel
我们尚未针对 Intel GPU 进行任何测试。
## 克隆仓库
要克隆仓库,您应该运行:
```
git clone --recursive https://github.com/HansKristian-Work/vkd3d-proton
```
以拉取构建所需的所有子模块。
## 构建 vkd3d-proton
### 要求:
- [wine](https://www.winehq.org/)(用于提供 `widl`)[用于本地构建]
- 在 Windows 上,可以将其替换为 [Strawberry Perl](http://strawberryperl.com/),因为它附带了 `widl` 并且易于查找和安装——尽管将来可能会移除这一依赖。
- [Meson](http://mesonbuild.com/) 构建系统(至少 0.49 版本)
- [glslang](https://github.com/KhronosGroup/glslang) 编译器
- [Mingw-w64](http://mingw-w64.org/) 编译器、头文件和工具(至少 7.0 版本)[用于默认的 d3d12.dll 交叉编译]
### 构建:
#### 简单方式
在 vkd3d-proton 目录中,运行:
```
./package-release.sh master /your/target/directory --no-package
```
这将在 `/your/target/directory` 中创建一个 `vkd3d-master` 文件夹,其中包含 vkd3d-proton 的 32 位和 64 位版本,可以按照上述发布版本的说明进行设置。
如果您想进行本地构建(即构建 `libvkd3d-proton.so`),请向构建脚本传递 `--native` 参数。此选项将使其使用您系统的编译器进行构建。
为了保留构建目录以供开发使用,请向脚本传递 `--dev-build` 参数。此选项隐含了 `--no-package`。在对源代码进行更改之后,您可以执行以下操作来重新构建 vkd3d-proton:
```
# 对于 32-bit,更改为 build.86
ninja -C /your/target/directory/build.64 install
```
#### 手动编译(交叉编译 d3d12.dll,默认)
```
# 64-bit build。
meson --cross-file build-win64.txt --buildtype release --prefix /your/vkd3d-proton/directory build.64
ninja -C build.64 install
# 32-bit build
meson --cross-file build-win32.txt --buildtype release --prefix /your/vkd3d-proton/directory build.86
ninja -C build.86 install
```
#### 手动编译(本地构建)
```
# 64-bit build。
meson --buildtype release --prefix /your/vkd3d-proton/directory build.64
ninja -C build.64 install
# 32-bit build
CC="gcc -m32" CXX="g++ -m32" \
PKG_CONFIG_PATH="/usr/lib32/pkgconfig:/usr/lib/i386-linux-gnu/pkgconfig:/usr/lib/pkgconfig" \
meson --buildtype release --prefix /your/vkd3d-proton/directory build.86
ninja -C build.86 install
```
#### 针对 aarch64 的交叉编译构建
首先,使用 Steam 的运行时环境设置一个 distrobox。
```
distrobox create --image registry.gitlab.steamos.cloud/steamrt/steamrt4/sdk/arm64-on-amd64 --name aarch64
distrobox enter aarch64
# Inside container
sudo apt install mingw-w64-tools
meson setup build-aarch64 \
--buildtype release \
--cross-file /usr/share/meson/cross/aarch64-linux-gnu-gcc.txt \
--cross-file ./build-widl.txt \
-Denable_extras=true \
-Denable_tests=true \
--prefix /tmp/vkd3d-proton-aarch64
ninja -C build-aarch64 install
cp build-aarch64/tests/d3d12 /tmp/vkd3d-proton-aarch64/bin
```
## 使用 vkd3d-proton
使用 vkd3d-proton 的预期方式是作为原生 Win32 DLL(d3d12.dll 和 d3d12core.dll)。
它们作为 D3D12 的直接替代品,可在 Wine(Proton 或原生版本)或 Windows 上使用。
vkd3d-proton 本身不提供必要的 DXGI 组件。
相反,DXVK(2.1+)和 vkd3d-proton 共享一个 DXGI 实现。
### 关于在 Windows 上使用 vkd3d-proton 的说明
原生 Windows 使用主要与开发人员测试目的相关。
不要指望在 Windows 7 或 8.1 上运行的游戏会奇迹般地使用 vkd3d-proton,
因为许多游戏只有在 Windows 10 上运行时才会尝试加载 d3d12.dll。
### 原生 Linux 构建
可以构建原生的 Linux 二进制文件,但其目的并不是与上游 Wine 兼容。
目前,原生选项主要与开发目的相关。
## 环境变量
vkd3d-proton 使用的大多数环境变量都是出于调试目的。这些
环境变量不被视为 API 的一部分,可能会在 vkd3d-proton 的未来版本中被更改或移除。
一些调试变量是元素列表。元素之间必须用逗号或分号分隔。
- `VKD3D_CONFIG` - 用于更改 vkd3d-proton 行为的选项列表。
- `vk_debug` - 启用 Vulkan 调试扩展并加载验证层。
- `skip_application_workarounds` - 跳过所有应用程序的变通方案。
用于调试目的。
- `nodxr` - 禁用 DXR 支持。
- `dxr` - DXR 通常会自动启用。此配置强制其启用,即使在被认为不安全的情况下也是如此。
- `dxr12` - 如果 `VK_EXT_opacity_micromap` 可用,则启用对 DXR 1.2 的实验性支持。
- `force_static_cbv` - NVIDIA 上不安全的速度提升 hack。可能会也可能不会带来显著的性能提升。
- `single_queue` - 不使用异步计算或传输队列。
- `no_upload_hvv` - 阻止任何尝试将主机可见 VRAM(大容量/可调整大小的 BAR)用于 UPLOAD 堆的操作。
在某些关键情况下可能会释放出至关重要的 VRAM,代价是降低 GPU 性能。
无论哪种情况,都会保留一部分 VRAM 用于可调整大小的 BAR 分配,
因此即使在低显存显卡上这也不应成为真正的问题。
- `force_host_cached` - 强制所有主机可见分配为 CACHED,这极大地加速了捕获。
- `no_invariant_position` - 避免针对不变位置使用变通方案。该变通方案默认启用。
- `VKD3D_DEBUG` - 控制 vkd3d-proton 生成的日志消息的调试级别。接受以下值:none、err、info、fixme、warn、trace。
- `VKD3D_SHADER_DEBUG` - 控制着色器编译器生成的日志消息的调试级别。有关可接受的值,请参见 `VKD3D_DEBUG`。
- `VKD3D_LOG_FILE` - 如果设置,将 `VKD3D_DEBUG` 的日志输出重定向到文件。
- `VKD3D_VULKAN_DEVICE` - 从零开始的设备索引。用于强制选择特定的 Vulkan 设备。
- `VKD3D_FILTER_DEVICE_NAME` - 跳过不包含此子字符串的设备。
- `VKD3D_DISABLE_EXTENSIONS` - 即使可用,vkd3d-proton 也不应使用的 Vulkan 扩展列表。
- `VKD3D_TEST_DEBUG` - 在测试中启用额外的调试消息。设置为 0、1 或 2。
- `VKD3D_TEST_MATCH` - 匹配字符串。只有名称与该字符串完全匹配的测试才会运行,例如 `VKD3D_TEST_FILTER=clear_render_target` 将只匹配名为 'clear_render_target' 的测试。
对调试或开发新测试很有用。
- `VKD3D_TEST_FILTER` - 过滤字符串。只有名称与过滤字符串匹配的测试才会运行,例如 `VKD3D_TEST_FILTER=clear_render` 将匹配名为 'clear_render_target' 或 'target_clear_render' 的测试。
对调试或开发新测试很有用。
- `VKD3D_TEST_EXCLUDE` - 排除名称包含在该字符串中的测试,例如 `VKD3D_TEST_EXCLUDE=test_root_signature_priority,test_conservative_rasterization_dxil`。
- `VKD3D_TEST_PLATFORM` - 可设置为 "wine"、"windows" 或 "other"。测试平台控制测试中 todo()、todo_if()、bug_if() 和 broken() 条件的行为。
- `VKD3D_TEST_BUG` - 设置为 0 以禁用测试中的 bug_if() 条件。
- `VKD3D_PROFILE_PATH` - 如果在构建中启用了性能分析,将向 `${VKD3D_PROFILE_PATH}.${pid}` 输出分析数据块。
- `VKD3D_SWAPCHAIN_PRESENT_MODE` - 接受一个 Vulkan 呈现模式名称。如果支持,强制使用指定的呈现模式。目前接受 `IMMEDIATE`、`MAILBOX`、`FIFO`、`FIFO_RELAXED`、`FIFO_LATEST_READY`。
### 帧率限制
`VKD3D_FRAME_RATE` 环境变量可用于限制帧率。值为 `0` 时解除帧率限制,而任何正值都会将渲染限制为给定的每秒帧数。
## 着色器缓存
默认情况下,vkd3d-proton 管理自己的驱动缓存。
此缓存旨在缓存 DXBC/DXIL -> SPIR-V 的转换。
这减少了卡顿(当管线在最后一刻创建且应用程序依赖热驱动缓存时)
和加载时间(当应用程序做足了预先加载 PSO 的正确步骤时)。
其行为设计接近于 DXVK 状态缓存。
#### 默认行为
`vkd3d-proton.cache`(以及 `vkd3d-proton.cache.write`)被放置在当前工作目录中。
通常,这是在 Steam 中运行时的游戏安装文件夹。
#### 自定义目录
`VKD3D_SHADER_CACHE_PATH=/path/to/directory` 覆盖放置 `vkd3d-proton.cache` 的目录。
#### 禁用缓存
`VKD3D_SHADER_CACHE_PATH=0` 禁用内部缓存,任何缓存都必须由应用程序显式管理。
### ID3D12PipelineLibrary 的行为
当使用显式着色器缓存时,对应用程序管理的管线库的需求大大降低,
并且应用程序与之交互的缓存是一个虚拟缓存。
如果 vkd3d-proton 的着色器缓存被禁用,ID3D12PipelineLibrary 将存储完整缓存所需的所有相关内容,
即 SPIR-V 和 PSO 驱动缓存 blob。
`VKD3D_CONFIG=pipeline_library_app_cache` 是 `VKD3D_SHADER_CACHE_PATH=0` 的替代方案,如果将来应用程序管理缓存比 vkd3d-proton 自动管理得更好,它可以根据应用程序配置自动启用。
## CPU 分析(开发)
向 Meson 传递 `-Denable_profiling=true` 以启用带性能分析的构建。使用带性能分析的构建时,需使用 `VKD3D_PROFILE_PATH` 环境变量。
性能分析会转储出一个二进制数据块,可以使用 `programs/vkd3d-profile.py` 进行分析。
性能分析是一个简易系统,记录迭代次数和花费的总时钟周期(ns)。
它可以很方便地检测您正在优化的代码部分。
## 高级着色器调试
这些功能仅供 vkd3d-proton 开发人员使用。对于任何内置的 RenderDoc 相关功能,
请向 Meson 传递 `-Denable_renderdoc=true`。
- `VKD3D_SHADER_DUMP_PATH` - 转储着色器字节码的路径。
字节码以 `$hash.{spv,dxbc,dxil}` 格式转储。
- `VKD3D_SHADER_OVERRIDE` - 可找到覆盖着色器的路径。
如果应用程序正在使用 `$hash` 创建管线,并且 `$VKD3D_SHADER_OVERRIDE/$hash.spv` 存在,
将转而使用该 SPIR-V 文件。
- `VKD3D_AUTO_CAPTURE_SHADER` - 如果将其设置为着色器哈希值,并且启用了 RenderDoc 层,
vkd3d-proton 将在遇到特定着色器时自动进行捕获。
- `VKD3D_AUTO_CAPTURE_COUNTS` - 逗号分隔的索引列表。这可用于控制要捕获哪些队列提交。
例如,使用 `VKD3D_AUTO_CAPTURE_COUNTS=0,4,10` 可捕获第 0 次(首次提交)、第 4 次和第 10 次作为候选的提交。
如果 `VKD3D_AUTO_CAPTURE_COUNTS` 为 `-1`,则整个应用程序运行时可以变成一个大型捕获。
这仅用于捕获诸如测试套件之类的内容,
或者具有有限运行时间的微型应用程序,以便更容易调试跨提交的工作。
如果仅设置了 `VKD3D_AUTO_CAPTURE_COUNTS`,则任何队列提交都会被视为捕获候选。
如果仅 `VKD3D_AUTO_CAPTURE_SHADER`,则 `VKD3D_AUTO_CAPTURE_COUNTS` 被视为等于 `"0"`,即仅在首次遇到目标着色器时进行捕获。
如果两者都设置了,则仅当提交包含目标着色器的使用时,捕获计数器才会递增并予以考虑。
### 面包屑调试 (Breadcrumbs debugging)
对于调试 GPU 挂起,了解崩溃发生的位置非常有用。
如果构建启用了跟踪功能(非发布版本),则也会启用面包屑支持。
`VKD3D_CONFIG=breadcrumbs` 将使用 `VK_AMD_buffer_marker` 或 `VK_NV_device_checkpoints` 检测命令列表。
在 GPU 设备丢失或超时时,崩溃转储将被写入日志。
为了在 RADV 上获得最佳结果,请使用 `RADV_DEBUG=syncshaders`。日志将打印当时正在执行的命令列表的摘要形式,
并尝试缩小可能导致崩溃的命令范围。
### 着色器日志
可以记录替换着色器的输出,本质上是一个自定义的着色器 printf。要启用此功能,必须支持 `VK_KHR_buffer_device_address`。
首先,例如使用 `VKD3D_SHADER_DEBUG_RING_SIZE_LOG2=28` 在主机内存中设置一个 256 MiB 的环形缓冲区。
由于此缓冲区是在主机内存中分配的,您可以随意将其设置得尽可能大,因为它不会消耗 VRAM。
一个工作线程将读取传入的数据并将其记录下来。未来有可能在这里发出更多结构化的信息。
实现此功能而不是使用验证层 printf 系统的主要原因是运行时性能,
并且避免了因引入增加锁等操作的验证层而意外掩盖任何 bug。
如果 `debugPrintEXT` 更适合您的调试场景,同样可以使用它。
通过这种着色器替换方案,我们能够尽可能无干扰地添加着色器日志。
```
# 在充满 override shaders 的文件夹内,使用以下命令 build 所有内容:
make -C /path/to/include/shader-debug M=$PWD
```
然后,该着色器可以包含 `#include "debug_channel.h"` 并使用下面的各种函数。
```
void DEBUG_CHANNEL_INIT(uvec3 ID);
```
在您替换的着色器中的某处使用。这应该使用 `gl_GlobalInvocationID` 或类似的方法进行初始化。
此 ID 将显示在日志中。对于每个调用 `DEBUG_CHANNEL_INIT` 的子组,都会生成一个实例计数器。
这允许您将多条源自相同实例计数器的消息关联起来,该计数器会与 ID 一起记录。
一个调用可以通过实例 + `DEBUG_CHANNEL_INIT` id 唯一标识。
`DEBUG_CHANNEL_INIT` 可以从非均匀控制流中调用,因为它不使用 `barrier()` 或类似结构。
因此,它也可以在顶点和片段着色器中使用。
```
void DEBUG_CHANNEL_MSG();
void DEBUG_CHANNEL_MSG(uint v0);
void DEBUG_CHANNEL_MSG(uint v0, uint v1, ...); // Up to 4 components, can be expanded as needed up to 16.
void DEBUG_CHANNEL_MSG(int v0);
void DEBUG_CHANNEL_MSG(int v0, int v1, ...); // Up to 4 components, ...
void DEBUG_CHANNEL_MSG(float v0);
void DEBUG_CHANNEL_MSG(float v0, float v1, ...); // Up to 4 components, ...
```
这些函数用于记录日志,格式为 uint 类型使用 `#%x`,int 类型使用 `%d`,float 类型使用 `%f`。
## 描述符调试
如果在构建时启用了 `-Denable_descriptor_qa=true`,您可以将 `VKD3D_DESCRIPTOR_QA_LOG` 环境变量设置为一个文件。
所有描述符的更新和复制都会被记录下来,以便将描述符与 GPU 崩溃转储关联起来。默认情况下不启用 `enable_descriptor_qa`,因为它在极其热门的代码路径中增加了一些直接开销。
### GPU 辅助调试
如果在启用 `-Denable_descriptor_qa=true` 的构建中设置了 `VKD3D_CONFIG=descriptor_qa_checks`,所有着色器都将被注入代码以检查无效访问。在日志中,您将看到此信息以确保该功能已启用。
```
932:info:vkd3d_descriptor_debug_init_once: Enabling descriptor QA checks!
```
主要动机是紧密的集成和高性能。
GPU 辅助调试可以以远超可玩速度的帧率运行。
#### 描述符堆索引越界
```
============
Fault type: HEAP_OUT_OF_RANGE
Fault type: MISMATCH_DESCRIPTOR_TYPE
CBV_SRV_UAV heap cookie: 1800
Shader hash and instruction: edbaf1b5ed344467 (1)
Accessed resource/view cookie: 0
Shader desired descriptor type: 8 (STORAGE_BUFFER)
Found descriptor type in heap: 0 (NONE)
Failed heap index: 1024000
==========
```
指令 `(1)` 也会被报告,
并且可以通过相关着色器的反汇编来准确定位哪里出了问题。
使用 `VKD3D_SHADER_DUMP_PATH=/my/folder` 转储所有着色器,
并运行 `spirv-cross -V /my/folder/edbaf1b5ed344467.spv`。
(注意:转储前请清空该文件夹,现有文件不会被覆盖)。
可以通过查看最后一个参数来识别出错的指令,例如:
```
uint fixup_index = descriptor_qa_check(heap_index, descriptor_type, 1u /* instruction ID */);
```
#### 不匹配的描述符类型
```
============
Fault type: MISMATCH_DESCRIPTOR_TYPE
CBV_SRV_UAV heap cookie: 1800 // Refer to VKD3D_DESCRIPTOR_QA_LOG
Shader hash and instruction: edbaf1b5ed344467 (1)
Accessed resource/view cookie: 1802 // Refer to VKD3D_DESCRIPTOR_QA_LOG
Shader desired descriptor type: 8 (STORAGE_BUFFER)
Found descriptor type in heap: 1 (SAMPLED_IMAGE)
Failed heap index: 1025
==========
```
#### 访问已销毁的资源
```
============
Fault type: DESTROYED_RESOURCE
CBV_SRV_UAV heap cookie: 1800
Shader hash and instruction: edbaf1b5ed344467 (2)
Accessed resource/view cookie: 1806
Shader desired descriptor type: 1 (SAMPLED_IMAGE)
Found descriptor type in heap: 1 (SAMPLED_IMAGE)
Failed heap index: 1029
==========
```
### 使用 RADV 转储调试描述符崩溃(硬核终极噩梦模式)
当您陷入绝对绝望的境地时,还有一种方法可以调试 GPU 挂起。
首先,安装 [umr](https://gitlab.freedesktop.org/tomstdenis/umr) 并将该二进制文件设置为 setuid。
`ACO_DEBUG=force-waitcnt RADV_DEBUG=hang VKD3D_DESCRIPTOR_QA_LOG=/somewhere/desc.txt %command%`
也可以使用 `RADV_DEBUG=hang,umr`,但在 Wine 内部会发生一些奇怪的事情,导致 UMR 转储并不总是成功。
相反,当 GPU 挂起时,可以通过 SSH shell 手动调用 umr。
```
#!/bin/bash
mkdir -p "$HOME/umr-dump"
# 对于 Navi,较旧的 GPU 可能有不同的 rings。参见 RADV 源码。
umr -R gfx_0.0.0 > "$HOME/umr-dump/ring.txt" 2>&1
umr -O halt_waves -wa gfx_0.0.0 > "$HOME/umr-dump/halt-waves-1.txt" 2>&1
umr -O bits,halt_waves -wa gfx_0.0.0 > "$HOME/umr-dump/halt-waves-2.txt" 2>&1
```
RADV 会在 `~/radv_dumps*` 中放置一个文件夹,而 UMR 脚本会将 wave 转储放置在 `~/umr-dump` 中。
首先,我们可以研究 wave 转储以查看崩溃发生的位置,例如:
```
pgm[6@0x800120e26c00 + 0x584 ] = 0xf0001108 image_load v47, v[4:5], s[48:55] dmask:0x1 dim:SQ_RSRC_IMG_2D unorm
pgm[6@0x800120e26c00 + 0x588 ] = 0x000c2f04 ;;
pgm[6@0x800120e26c00 + 0x58c ] = 0xbf8c3f70 s_waitcnt vmcnt(0)
* pgm[6@0x800120e26c00 + 0x590 ] = 0x930118c0 s_mul_i32 s1, 64, s24
pgm[6@0x800120e26c00 + 0x594 ] = 0xf40c0c09 s_load_dwordx8 s[48:55], s[18:19], s1
pgm[6@0x800120e26c00 + 0x598 ] = 0x02000000 ;;
```
excp: 256 是一个内存错误(至少在 5700xt 上是如此)。
```
TRAPSTS[50000100]:
excp: 256 | illegal_inst: 0 | buffer_oob: 0 | excp_cycle: 0 |
excp_wave64hi: 0 | xnack_error: 1 | dp_rate: 2 | excp_group_mask: 0 |
```
我们可以检查所有的 VGPR 和所有的 SGPR,这里是针对图像描述符的。
```
[ 48.. 51] = { 0130a000, c0500080, 810dc1df, 93b00204 }
[ 52.. 55] = { 00000000, 00400000, 002b0000, 800130c8 }
```
解码 VA(虚拟地址)并研究 `bo_history.log`。RADV 中有一个脚本可让您查询 VA 的历史记录。
这使我们能够验证相关 VA 在某个时刻已被释放。
在撰写本文时,还没有简单的方法可以解码原始的描述符 blob,但当您足够绝望时,您可以手动完成 :|
在 `pipeline.log` 中,我们有完整的 SPIR-V(带有指向源 DXIL/DXBC 的 OpSource 引用)
以及崩溃管线的反汇编。在这里我们可以研究代码以弄清楚读取了哪个描述符。
```
// s7 is the descriptor heap index, s1 is the offset (64 bytes per image descriptor),
// s[18:19] is the descriptor heap.
s_mul_i32 s1, 64, s7 ; 930107c0
s_load_dwordx8 s[48:55], s[18:19], s1 ; f40c0c09 02000000
s_waitcnt lgkmcnt(0) ; bf8cc07f
image_load v47, v[4:5], s[48:55] dmask:0x1 dim:SQ_RSRC_IMG_2D unorm ; f0001108 000c2f04
```
```
[ 4.. 7] = { 03200020, ffff8000, 0000002b, 00000103 }
```
即描述符索引 #259。基于此,我们可以检查描述符 QA 日志,并验证应用程序确实执行了无效操作,进而导致了 GPU 挂起。
标签:3D图形渲染, AMD RADV, Direct3D 12, DirectX 12兼容, Linux游戏, Mesa驱动, NVIDIA驱动, Proton, Steam Deck, UML, VKD3D, VKD3D-Proton, Vulkan, Wine, 兼容性层, 图形API转换, 客户端加密, 开源图形驱动, 技术栈:Vulkan 1.3, 游戏兼容层, 游戏移植, 翻译层, 跨平台游戏, 预握手