### Build vkd3d-proton for aarch64 Source: https://github.com/hanskristian-work/vkd3d-proton/blob/master/README.md Build vkd3d-proton for aarch64 cross-compilation. Ensure mingw-w64-tools are installed. ```bash 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 ``` -------------------------------- ### Workgraph Meta Shader Setup Source: https://github.com/hanskristian-work/vkd3d-proton/blob/master/docs/workgraphs.md This compute shader is used to set up GPU input for workgraph entry points. It is executed once per workgraph dispatch. ```glsl #define WORKGRAPH_SETUP_GPU_INPUT_COMP #include "workgraph_common.comp" layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in; void main() { uint idx = gl_GlobalInvocationID.x; if (idx >= D3D12_MAX_NODE_INPUTS) return; uint num_node_inputs = workgraph_dispatch_args.num_node_inputs; if (idx >= num_node_inputs) return; uint node_input_va = workgraph_dispatch_args.node_inputs[idx].entry_point; uint records = workgraph_dispatch_args.node_inputs[idx].records; uint num_records = workgraph_dispatch_args.node_inputs[idx].num_records; // Convert to 64-bit compute VA (PIPELINE token) uint pipeline_token = get_pipeline_token(node_input_va); // Convert to push constant update uint push_constant_token = get_push_constant_token(records); // Convert to 3D dispatch grid (DISPATCH token) uint dispatch_token = get_dispatch_token(num_records); // Store tokens in global memory workgraph_output.tokens[idx] = pipeline_token | push_constant_token | dispatch_token; } ``` -------------------------------- ### Shader Node Entry Point Wrapper Source: https://github.com/hanskristian-work/vkd3d-proton/blob/master/docs/workgraphs.md Illustrates a common wrapper function generated by dxil-spirv for shader nodes. It handles input payload setup and calls the actual node logic. This is necessary to bridge workgroup execution with shader node requirements. ```c++ PayloadPtr ptr; void real_node_main() { ... } void main() { convert_dispatch_grid(); ptr = set_input_payload(); real_node_main(); } ``` -------------------------------- ### NodeMaxDispatchGrid Shader Example Source: https://github.com/hanskristian-work/vkd3d-proton/blob/master/docs/workgraphs.md Demonstrates the use of NodeMaxDispatchGrid and NumThreads attributes in a HLSL shader for broadcasting nodes. This is useful for managing dynamic amplification in complex scenarios. ```hlsl RWStructuredBuffer RWBuf : register(u0); struct Payload { uint v; uint16_t2 size : SV_DispatchGrid; uint w; }; [Shader("node")] [NodeLaunch("broadcasting")] [NodeMaxDispatchGrid(3, 3, 2)] [NumThreads(2, 3, 4)] void BroadcastNode(DispatchNodeInputRecord payload, uint3 thr : SV_DispatchThreadID) { uint idx = thr.z * 100 + thr.y * 10 + thr.x; uint o; InterlockedAdd(RWBuf[idx], payload.Get().v ^ payload.Get().w, o); } ``` -------------------------------- ### Descriptor QA Check Function Example Source: https://github.com/hanskristian-work/vkd3d-proton/blob/master/README.md This is an example of how the descriptor_qa_check function might be used within a shader to validate heap index, descriptor type, and instruction ID. The third argument is the instruction ID. ```glsl uint fixup_index = descriptor_qa_check(heap_index, descriptor_type, 1u /* instruction ID */); ``` -------------------------------- ### Get Payload Pointer with Arithmetic and Casting Source: https://github.com/hanskristian-work/vkd3d-proton/blob/master/docs/workgraphs.md Demonstrates how to obtain a pointer to payload data using pointer arithmetic and casting after allocation. ```glsl uint _117 = AllocateThreadNodeRecords( NodeDispatch.NodePayloadOutputAtomicBDA, Broadcast0, Count, 12u, NodeDispatch.NodePayloadOutputOffset, NodeDispatch.NodePayloadOutputStride); uint _137 = Index * 12u; UserPointerType _141 = _132(NodeDispatch.NodePayloadOutputBDA + uint64_t(_117 + _137)); ``` -------------------------------- ### Amplification Case in Workgraphs Source: https://github.com/hanskristian-work/vkd3d-proton/blob/master/docs/workgraphs.md This GLSL code demonstrates an amplification technique for workgraphs. It calculates total amplification and loops through workgroups, setting WorkgroupID and GlobalInvocationID for each iteration. A barrier is used at the start of each workgroup. ```glsl layout(constant_id = 7) const uint MaxBroadcastGridX = 3u; layout(constant_id = 8) const uint MaxBroadcastGridY = 3u; layout(constant_id = 9) const uint MaxBroadcastGridZ = 2u; const uint _150 = (MaxBroadcastGridX * MaxBroadcastGridY); const uint _151 = (_150 * MaxBroadcastGridZ); for (uint amplification_index_2 = gl_WorkGroupID.z; amplification_index_2 < _151; amplification_index_2 += gl_NumWorkGroups.z) { uint _156 = amplification_index_2 / MaxBroadcastGridX; uvec3 _159 = uvec3(amplification_index_2 % MaxBroadcastGridX, _156 % MaxBroadcastGridY, _156 / MaxBroadcastGridY); WorkgroupID = _159; GlobalInvocationID = (ThreadGroupSize * _159) + gl_LocalInvocationID; node_main(); // At start of a workgroup, shaders can assume that it's safe to touch shared memory. barrier(); } ``` -------------------------------- ### Generate UMR Dumps for GPU Hangs Source: https://github.com/hanskristian-work/vkd3d-proton/blob/master/README.md This bash script generates UMR dumps for different GPU rings and wave states when a GPU hang occurs. Ensure UMR is installed and setuid. ```bash #!/bin/bash mkdir -p "$HOME/umr-dump" # For Navi, older GPUs might have different rings. See RADV source. 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 ``` -------------------------------- ### Implicit LOD with Helper Lanes Example Source: https://github.com/hanskristian-work/vkd3d-proton/blob/master/docs/sampler_feedback.md Illustrates how helper lanes can cause incorrect LOD computation when writing sampler feedback. The driver might insert a check for helper lanes before UV computation, leading to incorrect LOD values. ```c++ UV = compute_uv_in_quad_uniform_flow(); if (!is_helper) // Inserted by driver since helpers cannot have side effects. WriteFeedback(feedback, UV); // UV computation are probably sunk to a branch, leading to garbage LOD. ``` -------------------------------- ### Thread Node Workgroup Size Source: https://github.com/hanskristian-work/vkd3d-proton/blob/master/docs/workgraphs.md Defines the workgroup size for thread nodes in GLSL. This example hardcodes a size of 32, suitable for wave32, and includes logic for handling entry points and non-entry points, as well as partially full workgroups. ```glsl layout(local_size_x_id = 0, local_size_y_id = 1, local_size_z_id = 2) in; uint64_t NodeInputPayloadBDA; void node_main() { // App code } void main() { uint _77 = ((((gl_WorkGroupID.y * 32768u) + gl_WorkGroupID.x) * gl_WorkGroupSize.x) + gl_LocalInvocationIndex) + NodeDispatch.NodeLinearOffsetBDA.value; NodeInputPayloadBDA = NodeDispatch.PayloadLinearBDA; if (NodeIsProgramEntry) { // For entry points only. NodeInputStride = NodeDispatch.NodePayloadStrideOrOffsetsBDA.x; if (NodeEntryIndirectPayloadStride) { // GPU_INPUT node. Load addr + stride from the GPU buffer. NodeInputPayloadBDA = NodeReadonlyU64Ptr(NodeDispatch.PayloadLinearBDA).value; NodeInputStride = NodeReadonlyU32Ptr(NodeDispatch.NodePayloadStrideOrOffsetsBDA).value; } } if (NodeIsProgramEntry) { // Handle partially full workgroups. We'll only have one of those. if (_77 < NodeDispatch.NodeEndNodesBDA.value) { // Entry point path. Payload is given a simple strided array. NodeInputPayloadBDA += uint64_t(_77 * NodeInputStride); node_main(); } } else { // Handle partially full workgroups. We'll only have one of those. if (_77 < NodeDispatch.NodeEndNodesBDA.value) { // Non-entry points. Load payload offset from buffer. NodeInputPayloadBDA += uint64_t(NodeReadonlyU32ArrayPtr(NodeDispatch.NodePayloadStrideOrOffsetsBDA).offsets[_77]); node_main(); } } } ``` -------------------------------- ### Build vkd3d-proton (Simple Way) Source: https://github.com/hanskristian-work/vkd3d-proton/blob/master/README.md This script simplifies the release packaging process. Use --no-package to preserve build directories for development. ```bash ./package-release.sh master /your/target/directory --no-package ``` -------------------------------- ### Cross-compilation for aarch64 Source: https://github.com/hanskristian-work/vkd3d-proton/blob/master/README.md Set up a distrobox with Steam's runtime for aarch64 cross-compilation. This command creates and enters the necessary environment. ```bash distrobox create --image registry.gitlab.steamos.cloud/steamrt/steamrt4/sdk/arm64-on-amd64 --name aarch64 distrobox enter aarch64 ``` -------------------------------- ### Replay Native Build with d3d12-replayer Source: https://github.com/hanskristian-work/vkd3d-proton/blob/master/docs/single_dispatch_capture.md Use this command to replay a capture on a native Linux build. Ensure the path to the vkd3d-proton D3D12 library is correctly specified. ```shell d3d12-replayer --d3d12 /path/to/built/vkd3d-proton/libs/d3d12/libvkd3d-proton-d3d12.so --json /tmp/capture.json ``` -------------------------------- ### Replay on Windows with Native D3D12 Drivers Source: https://github.com/hanskristian-work/vkd3d-proton/blob/master/docs/single_dispatch_capture.md This command replays a capture on Windows using the system's native D3D12 drivers. The AgilitySDK DLLs must be placed in a 'D3D12/' subdirectory next to the replayer binary. ```shell d3d12-replayer --json /tmp/capture.json ``` -------------------------------- ### Compile Manually (Cross-compilation for d3d12.dll) Source: https://github.com/hanskristian-work/vkd3d-proton/blob/master/README.md Builds vkd3d-proton for Windows DLLs using cross-compilation. Specify the appropriate cross-file and build directory for 64-bit or 32-bit targets. ```bash # 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 ``` -------------------------------- ### Replay on Windows with vkd3d-proton Source: https://github.com/hanskristian-work/vkd3d-proton/blob/master/docs/single_dispatch_capture.md To replay a capture using vkd3d-proton on Windows, place vkd3d-proton's d3d12core.dll next to the replayer executable and use the --vkd3d-proton flag. This method avoids overriding the system's d3d12.dll. ```shell d3d12-replayer --json /tmp/capture.json --vkd3d-proton ``` -------------------------------- ### Rebuild vkd3d-proton after changes Source: https://github.com/hanskristian-work/vkd3d-proton/blob/master/README.md After modifying source code, use ninja to rebuild. Change to the appropriate build directory (e.g., build.64) for your target architecture. ```bash ninja -C /your/target/directory/build.64 install ``` -------------------------------- ### Clone vkd3d-proton Repository Source: https://github.com/hanskristian-work/vkd3d-proton/blob/master/README.md Use this command to clone the repository and its submodules, which are necessary for building. ```bash git clone --recursive https://github.com/HansKristian-Work/vkd3d-proton ``` -------------------------------- ### Enable Buffer Sync Validation for All Shaders Source: https://github.com/hanskristian-work/vkd3d-proton/blob/master/docs/syncval.md Use this configuration to enable buffer sync validation for every single shader. Be aware that this can be a performance concern. ```bash 0000000000000000-ffffffffffffffff sync ``` -------------------------------- ### Load and Validate BDA from Descriptor Heap Source: https://github.com/hanskristian-work/vkd3d-proton/blob/master/docs/syncval.md Demonstrates loading a Base Device Address (BDA) from a descriptor heap and validating memory access bounds using the loaded stride and image size. This ensures robust handling of bindless descriptors. ```glsl void main() { uint _43 = AllocateInvocationID(); InvocationID = _43; // Load the "BDA" from RTAS heap. uvec2 _52 = DescriptorHeapRobustness.descriptors[registers._m4]._m0[0u]; // Extract the stride. uint _54 = _52.y >> 16u; // Compute descriptor size for robustness purposes. We have to skip out of bounds load-store. uint _57 = uint(imageSize(_27[registers._m4])); bool _466 = ValidateBDALoadStore( BDA = _52, offset = gl_GlobalInvocationID.x * _54, size = _54, type = 0u, identifier = InvocationID, in_bounds = gl_GlobalInvocationID.x < _57); // Report if failure AssumeTrue(_466, instruction = 2u); } ``` -------------------------------- ### Build Override Shaders with Shader Debugging Source: https://github.com/hanskristian-work/vkd3d-proton/blob/master/README.md Use this command to build override shaders when the shader debug feature is enabled. Ensure you are in the folder containing the override shaders. ```bash make -C /path/to/include/shader-debug M=$PWD ``` -------------------------------- ### Compile Manually (Native Build) Source: https://github.com/hanskristian-work/vkd3d-proton/blob/master/README.md Builds vkd3d-proton for native Linux shared libraries. For 32-bit builds, ensure correct compiler flags and PKG_CONFIG_PATH are set. ```bash # 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 ``` -------------------------------- ### Enable Buffer Sync Validation for Compute Shaders Source: https://github.com/hanskristian-work/vkd3d-proton/blob/master/docs/syncval.md Use this configuration to enable buffer sync validation specifically for compute shaders, which is where these problems typically arise. ```bash 0000000000000000-ffffffffffffffff sync-compute ``` -------------------------------- ### Initialize Shader Debugging Channel Source: https://github.com/hanskristian-work/vkd3d-proton/blob/master/README.md Call this function at the beginning of your replaced shader to initialize the debug channel. It should be initialized with gl_GlobalInvocationID or a similar value to identify the shader invocation. ```glsl void DEBUG_CHANNEL_INIT(uvec3 ID); ``` -------------------------------- ### Comparative Timestamp Profiling Analysis Source: https://github.com/hanskristian-work/vkd3d-proton/blob/master/docs/single_dispatch_capture.md Compares two timestamp profiles to identify performance outliers between two drivers. Use --threshold to filter out short-running dispatches and focus on significant contributors to frame times. ```python python ./programs/vkd3d-timestamp-profile.py --first /tmp/a.csv --second /tmp/b.csv --threshold 0.1 --count 3 ``` -------------------------------- ### Analyze Pipeline Log for SPIR-V and Disassembly Source: https://github.com/hanskristian-work/vkd3d-proton/blob/master/README.md Study the pipeline log for full SPIR-V and disassembly of the crashed pipeline to identify the descriptor being read. ```assembly // 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 ``` -------------------------------- ### Enable Descriptor QA Checks Source: https://github.com/hanskristian-work/vkd3d-proton/blob/master/README.md This log message indicates that descriptor QA checks are being enabled. This requires `-Denable_descriptor_qa=true` to be set during the build. ```log 932:info:vkd3d_descriptor_debug_init_once: Enabling descriptor QA checks! ``` -------------------------------- ### GLSL RW Tracking Implementation Source: https://github.com/hanskristian-work/vkd3d-proton/blob/master/docs/workgraphs.md Provides a GLSL implementation for RW tracking, including atomic counter management and cross-group sharing synchronization. This is intended for use within node shaders. ```glsl bool FinishCrossGroupSharing(uint64_t CounterBDA) { // HLSL does not if (gl_LocalInvocationIndex == 0u) { uint _76 = atomicAdd(uintPointer(CounterBDA), uint(-1)); FinishCrossGroupSharingBroadcast = _76 == 1u; } barrier(); return FinishCrossGroupSharingBroadcast; } void node_main() { uint64_t _54 = NodeInputPayloadBDA; _51 _55 = _51(_54); uint _60 = atomicAdd(_55.data._m1, 1u); memoryBarrier(); barrier(); uint64_t _81 = NodeInputPayloadBDA; // We allocate a u32 counter at the end of node payload. // The offset is compile time constant based on the user payload type. // The value is written by internal metadata shader during payload expansion pass. bool _84 = FinishCrossGroupSharing(_81 + 8ul); if (_84) { uint _89 = atomicAdd(RWBuf0._m0[gl_LocalInvocationIndex], _55.data._m1); } } ``` -------------------------------- ### vkd3d_shader_node_input_push_signature Structure Source: https://github.com/hanskristian-work/vkd3d-proton/blob/master/docs/workgraphs.md Defines the structure for pushing input data to a shader node, including device addresses for various data buffers and configuration parameters. This structure is used to pass node-specific arguments and metadata. ```c struct vkd3d_shader_node_input_push_signature { VkDeviceAddress node_payload_bda; VkDeviceAddress node_linear_offset_bda; VkDeviceAddress node_total_nodes_bda; VkDeviceAddress node_payload_stride_or_offsets_bda; VkDeviceAddress node_payload_output_bda; VkDeviceAddress node_payload_output_atomic_bda; VkDeviceAddress local_root_signature_bda; uint32_t node_payload_output_offset; uint32_t node_payload_output_stride; uint32_t node_remaining_recursion_levels; }; ``` -------------------------------- ### Allocate Thread Node Records Waterfall Loop in GLSL Source: https://github.com/hanskristian-work/vkd3d-proton/blob/master/docs/workgraphs.md Handles per-thread allocations when the node index is not wave uniform using a waterfall loop. Requires VK_KHR_maximal_reconvergence for technical correctness. ```glsl uint AllocateThreadNodeRecordsWaterfall( uint64_t AtomicCountersBDA, uint NodeMetadataIndex, uint Count, uint Stride, uint AllocationOffset) { uint _128; for (;;) { uint _126 = subgroupBroadcastFirst(NodeMetadataIndex); if (_126 == NodeMetadataIndex) { _128 = AllocateThreadNodeRecords(AtomicCountersBDA, _126, Count, Stride, AllocationOffset); break; } continue; } return _128; } ``` -------------------------------- ### GLSL Workgroup Size Estimation Source: https://github.com/hanskristian-work/vkd3d-proton/blob/master/docs/workgraphs.md Estimates the initial WorkGroupSize.z by reducing amplification based on the number of workgroups. This aims to balance work spawned on the GPU. ```glsl // Try to balance work we spawn on the GPU. // AMPLIFICATION_EXTRA_SHIFT is an arbitrarily chosen value. amplification = max(1u, amplification >> findMSB(max(total_wgs >> AMPLIFICATION_EXTRA_SHIFT, 1u))); ``` -------------------------------- ### Broadcast Node Shader Preamble (GLSL) Source: https://github.com/hanskristian-work/vkd3d-proton/blob/master/docs/workgraphs.md GLSL preamble for broadcast node shaders. It determines the input payload base address and stride, handling program entry and indirect payload stride cases. ```glsl uint _94 = DispatchStaticPayload ? NodeDispatch.NodeLinearOffsetBDA.value : (((gl_WorkGroupID.y * 32768u) + gl_WorkGroupID.x) + NodeDispatch.NodeLinearOffsetBDA.value); NodeInputPayloadBDA = NodeDispatch.PayloadLinearBDA; if (NodeIsProgramEntry) { NodeInputStride = NodeDispatch.NodePayloadStrideOrOffsetsBDA.x; if (NodeEntryIndirectPayloadStride) { NodeInputPayloadBDA = NodeReadonlyU64Ptr(NodeDispatch.PayloadLinearBDA).value; NodeInputStride = NodeReadonlyU32Ptr(NodeDispatch.NodePayloadStrideOrOffsetsBDA).value; } } ``` -------------------------------- ### GLSL Compaction Broadcast Logic Source: https://github.com/hanskristian-work/vkd3d-proton/blob/master/docs/workgraphs.md Implements logic for compaction broadcast when the number of dispatched records is less than 32k. It uses atomic operations to manage counters for indirect commands. ```glsl if (should_compact_broadcast) { memoryBarrierBuffer(); barrier(); if (gl_LocalInvocationIndex == 0) { restrict IndirectCommandsBufferAtomic atomics = IndirectCommandsBufferAtomic(registers.commands); uint counter = atomicAdd(atomics.indirect_commands_atomic[registers.node_index].expander_workgroup_counter, 1u); if (counter + 1 == gl_NumWorkGroups.x) { // Reset counter for next iteration, need atomic to ensure it's a coherent write. atomicAnd(atomics.indirect_commands_atomic[registers.node_index].expander_workgroup_counter, 0u); // Exchange to get coherent read and reset to 0, nifty. uint total_groups = atomicExchange(atomics.indirect_commands_atomic[registers.node_index].expander_total_groups, 0u); uint wgx = atomicOr(atomics.indirect_commands_atomic[registers.node_index].secondary_execute.x, 0u); ``` -------------------------------- ### Indirect Dispatch for Large Workgroup Counts Source: https://github.com/hanskristian-work/vkd3d-proton/blob/master/docs/workgraphs.md Demonstrates a strategy for dispatching a large number of workgroups (N) using two indirect dispatches to overcome the 2^16-1 limit per dimension. This method ensures all workgroups are processed even for very large N. ```glsl (2^15, N / 2^15, 1) (N % 2^15, 1, 1) ``` -------------------------------- ### Calculate Prefix Sum for Payload Offsets Source: https://github.com/hanskristian-work/vkd3d-proton/blob/master/docs/workgraphs.md Performs an inclusive prefix sum on the total workgroup counts within a subgroup and shuffles the final sum to all invocations. This is used to determine payload offsets. ```glsl uint scan = subgroupInclusiveAdd(counts.total); uint total_scan = subgroupShuffle(scan, gl_SubgroupSize - 1); scan -= counts.total; ``` -------------------------------- ### Set RADV Debugging Environment Variables Source: https://github.com/hanskristian-work/vkd3d-proton/blob/master/README.md Set environment variables to enable hang detection and log descriptor QA information for debugging GPU crashes. ```bash ACO_DEBUG=force-waitcnt RADV_DEBUG=hang VKD3D_DESCRIPTOR_QA_LOG=/somewhere/desc.txt %command% ``` -------------------------------- ### C++: D3D12_NODE_GPU_INPUT Structure Source: https://github.com/hanskristian-work/vkd3d-proton/blob/master/docs/workgraphs.md Defines the structure for GPU node inputs, containing the entry point index, number of records, and GPU virtual address and stride for records. This is used when payload data is linear in memory. ```cpp typedef struct D3D12_NODE_GPU_INPUT { UINT EntrypointIndex; UINT NumRecords; D3D12_GPU_VIRTUAL_ADDRESS_AND_STRIDE Records; } D3D12_NODE_GPU_INPUT; ```