A method for calculating Direct3D 12 derivatives based on hardware-assisted sampling
By simulating Direct3D 12 shader derivative operations on GPUs that do not support VK_KHR_compute_shader_derivatives using hardware-assisted sampling, the problems of high rendering error rate, large performance loss and texture sampling accuracy loss are solved. This achieves lossless downgrade compatibility with high-version shaders and meets the real-time rendering requirements of AAA games.
Patent Information
- Authority / Receiving Office
- CN · China
- Patent Type
- Patents(China)
- Current Assignee / Owner
- 北京麟卓信息科技有限公司
- Filing Date
- 2026-03-12
- Publication Date
- 2026-05-15
AI Technical Summary
Existing technologies suffer from high rendering error rates, significant performance degradation, loss of texture sampling precision, and insufficient compatibility with higher version shaders on GPUs that do not support the VK_KHR_compute_shader_derivatives extension, thus failing to meet the real-time rendering requirements of AAA games.
By using a hardware-assisted sampling method, the derivative operations of Direct3D 12 shader are simulated. This includes initializing the Quads or Linear mode engine, constructing the SPIR-V instruction sequence, rewriting the derivative function, optimizing the shared memory layout, combining the Vulkan computation pipeline layout, binding the command buffer, and using GPU hardware assistance to complete the derivative calculation and texture sampling.
It improves rendering accuracy and performance, reduces rendering error rate, enhances the performance of compute shaders, meets the real-time rendering requirements of AAA games, and achieves lossless downgrade compatibility with higher version shaders.
Smart Images

Figure CN121858110B_ABST
Abstract
Description
Technical Field
[0001] This invention belongs to the field of computer software development technology, specifically relating to a method for calculating Direct3D 12 derivatives based on hardware-assisted sampling. Background Technology
[0002] VKD3D is an open-source project that simulates Direct3D 12 (hereinafter referred to as D3D12) based on the Vulkan 1.3 standard. Its compatibility with D3D12 compute shaders highly depends on the VK_KHR_compute_shader_derivatives extension. The core functionality of this extension includes two execution modes: two-dimensional derivative calculation DerivativeGroupQuadsKHR with 2x2 quadruples and one-dimensional derivative calculation DerivativeGroupLinearKHR with linear thread groups. It achieves efficient calculation of derivative functions through hardware-level inter-thread data sharing, and deeply binds the derivative results with operations such as LOD calculation of texture sampling and anisotropic filtering to ensure semantic consistency of D3D12 Shader Model 6.6 and above.
[0003] In existing technologies, the mainstream solution for GPUs that do not support this extension is pure software simulation and shader downgrade compilation. However, this approach has the following key drawbacks: First, execution mode replication is lacking. Adaptation logic for the dual execution modes of VK_KHR_compute_shader_derivatives is not designed, and only a single shared memory synchronous simulation of derivatives is used. This leads to semantic misalignment between derivative calculations for 2D texture sampling and linear memory access in D3D12, resulting in a rendering error rate exceeding 70%. Second, performance overhead is excessive. Pure software simulation relies on the global memory barrier vkCmdPipelineBarrier for thread synchronization, causing shader performance to drop by more than 60% compared to hardware-supported scenarios, failing to meet the real-time rendering requirements of AAA games. Third, texture sampling precision is lost. Manually calculated LOD values are isolated from hardware sampling unit parameters due to floating-point errors, leading to texture flickering, incorrect Mipmap level selection, and other issues, especially noticeable in anisotropic filtering scenarios. Fourth, there is insufficient compatibility with higher version shaders, particularly ShaderModel 6.6. The above-mentioned attributes lacked adaptation logic, and after being downgraded to version 6.5, many advanced rendering technologies that rely on derivatives could not be enabled.
[0004] Furthermore, the existing solution does not incorporate the Linux kernel's Vulkan command scheduling mechanism for optimization, but only modifies the VKD3D application layer. This results in a mismatch between the execution scheduling of GPU threads and the 2x2 thread group requirements of derivative simulation, further amplifying the performance loss. Summary of the Invention
[0005] In view of this, the present invention provides a method for calculating Direct3D 12 derivatives based on hardware-assisted sampling. This method simulates the calculation of shader derivatives in Direct3D 12 (D3D12) using the Vulkan 1.3 architecture. It is particularly suitable for GPU hardware scenarios that do not support the VK_KHR_compute_shader_derivatives extension, and solves the rendering correctness and performance bottleneck problems of the VKD3D open source project on older GPUs.
[0006] This invention provides a method for calculating Direct3D 12 derivatives based on hardware-assisted sampling, which specifically includes the following steps:
[0007] After VKD3D and D3D applications start, when the application creates a compute shader, VKD3D traverses the abstract syntax tree of the D3D12 compute shader, sets the execution mode flag of the obtained derivative function VK_KHR_compute_shader_derivatives, and establishes the first association table between the texture sampler, derivative function parameters, and sampling context.
[0008] When the execution mode is marked as Quads mode, the Quads mode engine simulating DerivativeGroupQuadsKHR is initialized, and the SPIR-V instruction sequence is constructed according to whether the GPU supports subgroup operations and the subgroup size; when the execution mode is marked as Linear mode, the Linear mode engine simulating DerivativeGroupLinearKHR is initialized, and the SPIR-V instruction sequence is constructed; the derivative function of D3D12 is rewritten using the SPIR-V instruction sequence to obtain the derivative simulation logic and Vulkan shader module;
[0009] Obtain the original workgroup dimension of the D3D12 compute shader. Determine the target subgroup dimension and subgroup thread count by the execution mode flag. The original workgroup dimension becomes a workgroup dimension that includes the base dimension and the subgroup, satisfying that the product of each dimension of the workgroup and the product of the subgroup thread count are equal to the product of the original workgroup dimension after pairing. Divide the current subgroup according to the characteristics of the GPU subgroup and map it to a Vulkan subgroup, sharing the memory layout configuration.
[0010] In the Vulkan compute pipeline layout, a set of derivative parameter descriptors is merged, including a unified buffer containing the first association table, a storage buffer containing the derivative calculation results, and a texture buffer containing the GPU floating-point error compensation values. This results in the Vulkan pipeline layout. A Vulkan compute pipeline containing derivative simulation logic is created and bound to the command buffer. When the application records a texture sampling command, the native sampling function is modified into a sampling proxy function. The command buffer is submitted to the GPU queue. The GPU calculates the derivative results using the derivative simulation logic and writes them to the storage buffer. The sampling proxy function calculates the LOD value based on the derivative results, constructs the sampling parameter structure, and writes it to the Vulkan push constant, which is then written to the GPU sampling unit hardware register by the kernel. The command buffer is then resubmitted to the GPU queue, and the GPU sampling unit completes the rendering.
[0011] Furthermore, the method for marking the execution mode of the VK_KHR_compute_shader_derivatives of the obtained derivative function is as follows: if the derivative array attribute DerivativeGroupQuads exists, or the derivative function is used for 2D texture coordinate calculation or processing 2D mesh data, then the derivative function is marked as Quads mode; if the derivative array attribute DerivativeGroupLinear exists, or the derivative function is used for linear memory access or processing 1D data, then the derivative function is marked as Linear mode.
[0012] Furthermore, the method for constructing the SPIR-V instruction sequence based on whether the GPU supports subgroup operations and the subgroup size is as follows: when the GPU supports subgroup operations and the subgroup size is not less than a set value, a SPIR-V instruction sequence consisting of subgroup memory binding, thread result storage, subgroup synchronization, adjacent thread result reading, and differential calculation is constructed; when the GPU only supports shared memory, a SPIR-V instruction sequence consisting of shared memory declaration, shared memory address calculation, thread result storage, shared memory synchronization, adjacent thread address calculation, adjacent thread result reading, and differential calculation is constructed.
[0013] Furthermore, when the execution mode is marked as Linear mode, the Linear mode engine simulating DerivativeGroupLinearKHR is initialized, and the SPIR-V instruction sequence is constructed in the following way: a SPIR-V instruction sequence consisting of shared memory declaration, linear address calculation, thread result storage, shared memory synchronization, next thread address calculation, next thread result reading, and differential calculation is constructed.
[0014] Furthermore, the original workgroup dimension is transformed into a workgroup dimension that includes a base dimension and subgroups by calculating the base dimension by rounding up and dividing by the length of the corresponding dimension of the subgroup. The remaining dimensions remain consistent with the original workgroup. For the dimensions that need to be split, a parity check is performed. If the dimension is odd, 1 is added to make it even.
[0015] Furthermore, the method of dividing the current subgroup according to the characteristics of the GPU subgroup and mapping it to the Vulkan subgroup is as follows: for Quads mode, the division method of the current subgroup is determined according to the size of the GPU subgroup and then the resulting subgroup is mapped to the Vulkan subgroup; for Linear mode, the grouping method is dynamically determined according to the size of the GPU subgroup under the condition of thread continuity within the subgroup and then the resulting subgroup is mapped to the Vulkan subgroup.
[0016] Furthermore, the method for determining the division of the current subgroup based on the GPU subgroup size and then mapping the resulting subgroup to a Vulkan subgroup in Quads mode is as follows: when the GPU subgroup size is 4, the current subgroup is mapped to a Vulkan subgroup; when the GPU subgroup size is 8, the 8 threads are divided into two 2x2 subgroups and then mapped to Vulkan subgroups, with synchronization achieved through shared memory within the subgroups; when the GPU subgroup size is 32, the 32 threads are divided into eight 2x2 subgroups and then mapped to Vulkan subgroups, with each subgroup corresponding to an independent block of shared memory.
[0017] Furthermore, for Linear mode, the grouping method is dynamically determined based on the GPU subgroup size while ensuring thread continuity within the subgroup. Specifically: when the GPU subgroup size is 2, the current subgroup is mapped to a Vulkan subgroup; when the GPU subgroup size is 4, the 4 current subgroup threads are split into 2 independent Linear subgroups, each containing 2 consecutive threads; when the GPU subgroup size is 8, the 8 current subgroup threads are split into 4 Linear subgroups, each maintaining a linear order of 2 consecutive threads; when the GPU subgroup size is 32, the 32 current subgroup threads are first divided into 4 thread blocks of 8 threads each, and then each thread block is further divided into 4 Linear subgroups of 2 consecutive threads each.
[0018] Furthermore, in Linear mode, when the GPU subgroup size is 2, the thread index mapping rule within the subgroup is `subgroup_linear_id = gl_LocalInvocationID.x % 2`, where `subgroup_linear_id` is the thread index of the subgroup, and `gl_LocalInvocationID.x` is a compute shader built-in variable representing the X-axis index of the thread within the local workgroup. When the GPU subgroup size is 4, the linear offset `linear_offset` of the thread within the subgroup is calculated as `gl_LocalInvocationID.x % 4`, the Linear subgroup ID is assigned as `linear_offset / 2`, and the relative ID within the subgroup is determined as `linear_offset % 2`. When the GPU subgroup size is 8, the linear offset `linear_offset` of the thread within the subgroup is calculated as `gl_LocalInvocationID.x % 8`, and the Linear subgroup ID is assigned as `linear_offset / 2`.
[0019] Furthermore, the GPU calculates the derivative result through derivative simulation logic as follows: each thread calculates its position and subgroup identifier within its subgroup, and reads the first association table in the derivative parameter descriptor set to confirm that the parameter to be processed is associated with the texture sampling; each thread stores thread data through subgroup memory and shared memory, and calls synchronization instructions to ensure data visibility; based on the position of the thread within the subgroup, it reads data from adjacent threads to complete the calculation of the X-direction derivative and the Y-direction derivative; it reads the error compensation value in the texture buffer of the derivative parameter descriptor set to correct floating-point operation errors and obtain the derivative result.
[0020] Beneficial effects:
[0021] This invention loads the sampling unit register address mapping table of mainstream GPUs after system startup. When VKD3D and D3D applications start and the application creates a device, VKD3D creates a Vulkan logical device. When creating a compute shader, VKD3D traverses the shader abstract syntax tree to extract relevant information, marks the corresponding Quads or Linear execution modes, establishes an association table between the texture sampler, derivative function parameters, and sampling context, and stores relevant feature information in the shader's extended fields. Based on the execution mode, it initializes the corresponding simulation engine, generates an SPIR-V instruction sequence adapted to the GPU hardware capabilities, rewrites the D3D12 derivative function, obtains the Vulkan shader module, and completes workgroup dimension splitting, GPU subgroup feature adaptation, and shared memory layout optimization according to the execution mode. It integrates the derivative parameter descriptor set containing the association table, derivative results, and GPU floating-point error compensation values to construct the Vulkan pipeline layout, based on... The shader module, pipeline layout, and subgroup configuration create a Vulkan computation pipeline containing derivative simulation logic and bind it to the command buffer. Dispatch commands from the computation shader are recorded to the command buffer. When recording texture sampling commands, the native sampling function is replaced with a sampling proxy function. After submitting the command buffer, the GPU first executes the computation shader, runs the rewritten derivative calculation instructions to complete derivative calculation, precision correction, and exception checking, and writes the derivative result to the storage buffer. Based on the derivative result, an associated cache is built. The sampling proxy function reads the corresponding derivative result and calculates the LOD value according to the native extended hardware logic, packages the sampling parameters and writes them to the Vulkan push constant. The kernel intercepts commands containing texture sampling, parses the parameters, and writes the derivative result and LOD value to the GPU sampling unit hardware register for hardware-assisted sampling. The parameter injection flag in the command buffer is updated and resubmitted to the GPU queue, and the GPU sampling unit completes rendering. Attached Figure Description
[0022] Figure 1 This is a flowchart illustrating a Direct3D 12 derivative calculation method based on hardware-assisted sampling provided by the present invention. Detailed Implementation
[0023] The present invention will be described in detail below with reference to the accompanying drawings and embodiments.
[0024] This invention provides a method for Direct3D 12 derivative calculation based on hardware-assisted sampling. The core idea is as follows: After system startup, the sampling unit register address mapping table of mainstream GPUs is loaded. When VKD3D and D3D applications start and the application creates a device, VKD3D creates a Vulkan logical device. When creating a computation shader, VKD3D traverses the shader abstract syntax tree to extract relevant information, marks the corresponding Quads or Linear execution modes, establishes an association table between the texture sampler, derivative function parameters, and sampling context, and stores relevant feature information in the shader's extended fields. The corresponding simulation engine is initialized according to the execution mode, generating an SPIR-V instruction sequence adapted to the GPU hardware capabilities and rewriting the D3D12 derivative function to obtain the Vulkan shader module. The workgroup dimension is split, GPU subgroup feature adaptation, and shared memory layout optimization are completed according to the execution mode. Finally, a derivative parameter descriptor set containing the association table, derivative results, and GPU floating-point error compensation values is integrated to construct the Vulkan logical device. The Vulkan pipeline layout, based on shader modules, pipeline layout, and subgroup configurations, creates a Vulkan computation pipeline containing derivative simulation logic and binds it to the command buffer. Dispatch commands from the computation shaders are recorded to the command buffer. When recording texture sampling commands, the native sampling function is replaced with a sampling proxy function. After submitting the command buffer, the GPU first executes the computation shader, runs the rewritten derivative function to complete derivative calculation, precision correction, and anomaly checking, and writes the derivative result to the storage buffer. An associated cache is built based on the derivative result. The sampling proxy function reads the corresponding derivative result and calculates the LOD value according to the native extended hardware logic, packages the sampling parameters and writes them to the Vulkan push constant. The kernel intercepts commands containing texture sampling, parses the parameters, and writes the derivative result and LOD value to the GPU sampling unit hardware register. The parameter injection flag in the command buffer is updated and resubmitted to the GPU queue, and the GPU sampling unit completes rendering.
[0025] This invention provides a method for calculating Direct3D 12 derivatives based on hardware-assisted sampling, the processing flow of which is as follows: Figure 1 As shown, the specific steps include:
[0026] Step 1: After the system starts, the sampling unit register address mapping table of the mainstream GPU is loaded; after the VKD3D and D3D applications start, VKD3D creates a Vulkan logical device when the D3D application creates a device.
[0027] Step 2: When creating a computation shader in a D3D application, VKD3D uses the HLSL front-end parser to traverse the abstract syntax tree of the D3D12 computation shader to obtain the derivative function, texture sampling function, shader model version, and derivative array attributes. Based on the application scenario and derivative array attributes of the derivative function, the execution mode of VK_KHR_compute_shader_derivatives corresponding to the derivative function is marked, including Quads mode and Linear mode. A first association table is established between the texture sampler, derivative function parameters, and sampling context to record the complete dependency relationship of each texture sampling operation, including texture sampler ID, dependent derivative function, derivative parameter type, sampling function type, and associated subgroup ID. The associated subgroup ID is the identifier of the derivative calculation subgroup that the sampling operation depends on. The extracted derivative function list, execution mode marker, first association table, and shader model version are added to the first extended field of the D3D12 computation shader.
[0028] Specifically, based on the application scenario and derivative array attributes of the derivative function, the execution mode of the corresponding VK_KHR_compute_shader_derivatives is marked as follows: if the derivative array attribute DerivativeGroupQuads exists, or the derivative function is used for 2D texture coordinate calculation or processing 2D mesh data, then the derivative function is marked as Quads mode; if the derivative array attribute DerivativeGroupLinear exists, or the derivative function is used for linear memory access or processing 1D data, then the derivative function is marked as Linear mode.
[0029] Step 3: VKD3D initializes the Quads mode engine for simulating DerivativeGroupQuadsKHR based on the execution mode flag in the first extended field. If it is Quads mode, it initializes the Quads mode engine for simulating DerivativeGroupQuadsKHR, which includes a thread index mapper, a shared memory allocator, and a synchronization instruction generator. When the GPU supports subgroup operations and the subgroup size is not less than a set value, it constructs an SPIR-V instruction sequence consisting of subgroup memory binding, thread result storage, subgroup synchronization, adjacent thread result reading, and differential calculation. When the GPU only supports shared memory, it constructs an SPIR-V instruction sequence consisting of shared memory declaration, shared memory address calculation, thread result storage, shared memory synchronization, adjacent thread address calculation, adjacent thread result reading, and differential calculation. If it is Linear mode, it initializes the Linear mode engine for simulating DerivativeGroupLinearKHR, which includes a linear thread sorter, a linear shared memory layout, and a linear synchronization instruction generator. It constructs an SPIR-V instruction sequence consisting of shared memory declaration, linear address calculation, thread result storage, shared memory synchronization, next thread address calculation, next thread result reading, and differential calculation.
[0030] The derivative function of D3D12 is rewritten using the constructed SPIR-V instruction sequence, resulting in the rewritten SPIR-V code, namely the derivative simulation logic and Vulkan shader module.
[0031] Step 4: VKD3D obtains the original workgroup dimension of the D3D12 compute shader to be created. Based on the execution mode flag, it determines the target subgroup dimension and the number of threads per subgroup. For the dimensions to be split, the base dimension is calculated by rounding up and dividing by the length of the corresponding subgroup dimension. The remaining dimensions remain consistent with the original workgroup. Parity checks are performed on the dimensions to be split. If the number is odd, 1 is added to make it even to ensure that each subgroup is complete. The split workgroup dimension consists of the calculated base dimension and subgroup. Each thread of the base dimension corresponds to one subgroup, and the total number of threads satisfies that the product of the workgroup dimension and the number of threads per subgroup is equal to the product of the original workgroup dimension after padding.
[0032] The subgroup characteristics of the GPU are obtained. For Quads mode, the subgroup division method is determined based on the GPU subgroup size, and the obtained subgroups are then mapped to Vulkan subgroups. For Linear mode, the grouping method is dynamically determined based on the GPU subgroup size while ensuring thread continuity within the subgroup, and the obtained subgroups are then mapped to Vulkan subgroups. For GPUs that only support shared memory, a 2x2 block aligned shared memory layout is established to form a shared memory layout configuration.
[0033] Specifically, for Quads mode, the method of determining the division of the current subgroup based on the GPU subgroup size and then mapping the resulting subgroups to Vulkan subgroups is as follows: when the GPU subgroup size is 4, the current subgroup is mapped to a Vulkan subgroup; when the GPU subgroup size is 8, the 8 threads are divided into two 2x2 subgroups and then mapped to Vulkan subgroups, with synchronization achieved through shared memory within the subgroups; when the GPU subgroup size is 32, the 32 threads are divided into eight 2x2 subgroups and then mapped to Vulkan subgroups, with each subgroup corresponding to an independent block of shared memory.
[0034] For Linear mode, the grouping method is dynamically determined based on the GPU subgroup size while ensuring thread continuity within the subgroup. The resulting subgroups are then mapped to Vulkan subgroups. Specifically: when the GPU subgroup size is 2, the current subgroup is mapped to a Vulkan subgroup; when the GPU subgroup size is 4, the 4 current subgroup threads are split into 2 independent Linear subgroups, each containing 2 consecutive threads; when the GPU subgroup size is 8, the 8 current subgroup threads are split into 4 Linear subgroups, each maintaining a linear order of 2 consecutive threads; when the GPU subgroup size is 32, the 32 current subgroup threads are first divided into 4 thread blocks of 8 threads each, and then each thread block is further divided into 4 Linear subgroups of 2 consecutive threads each.
[0035] Furthermore, in Linear mode, when the GPU subgroup size is 2, the thread index mapping rule within the subgroup is `subgroup_linear_id = gl_LocalInvocationID.x % 2`, where `subgroup_linear_id` is the thread index of the subgroup, and `gl_LocalInvocationID.x` is a compute shader built-in variable representing the X-axis index of the thread within the local workgroup. When the GPU subgroup size is 4, the linear offset `linear_offset` of the thread within the subgroup is calculated as `gl_LocalInvocationID.x % 4`, the Linear subgroup ID is assigned as `linear_offset / 2`, and the relative ID within the subgroup is determined as `linear_offset % 2`. When the GPU subgroup size is 8, the linear offset `linear_offset` of the thread within the subgroup is calculated as `gl_LocalInvocationID.x % 8`, and the Linear subgroup ID is assigned as `linear_offset / 2`.
[0036] Furthermore, for GPUs that only support shared memory, a 2x2 block-aligned shared memory layout is established, dividing the shared memory into multiple contiguous 2x2 sub-blocks. The size of each block is the product of the largest number of bytes of the derivative parameter type and 4. The shared memory address calculation formula is shared_addr = (subgroup_id * 4 + quad_local_id) *param_size, where subgroup_id = gl_WorkGroupID.x * X_base + gl_WorkGroupID.y, X_base is the X value of the base dimension, and param_size is the number of bytes of the derivative parameter type, ensuring that the four threads of each subgroup access contiguous memory blocks. The starting address of each sub-block is aligned to the GPU's cache line size.
[0037] Step 5: In the Vulkan computation pipeline layout, merge the derivative parameter descriptor set including the unified buffer storing the first association table, the storage buffer storing the derivative calculation results, and the texture buffer storing the GPU floating-point error compensation values, etc., to obtain the Vulkan pipeline layout; use the obtained Vulkan shader module, Vulkan pipeline layout, basic dimension and basic dimension and Vulkan subgroup, and shared memory layout configuration to create a Vulkan computation pipeline containing derivative simulation logic, and bind the Vulkan computation pipeline to the command buffer.
[0038] Step 6: VKD3D records the Dispatch command of the computation shader to the command buffer. When applying the recorded texture sampling command, VKD3D modifies the native sampling function to a sampling proxy function. The command buffer is submitted to the GPU queue. The GPU first executes the computation shader. When it reaches the rewritten derivative calculation SPIR-V instruction, the thread calculates its position and subgroup identifier within its subgroup, and reads the first association table in the derivative parameter descriptor set to confirm that the parameter to be processed is associated with the texture sampling. Each thread stores thread data through subgroup memory and shared memory, and calls synchronization instructions to ensure data visibility. Based on the thread's position within the subgroup, it reads data from adjacent threads to complete the calculation of the X-direction derivative and Y-direction derivative. It reads the error compensation value in the texture buffer of the derivative parameter descriptor set to correct floating-point operation errors and obtain the derivative result. It checks the results within the subgroup to mark calculation anomalies, and writes the normal derivative result to the storage buffer.
[0039] Step 7: VKD3D initializes the associated cache using derivative results within the texture processing module. The associated cache is a hash table structure with texture, sampler, and subgroup identifiers as keys and derivative results as values. The sampling proxy function reads the corresponding derivative results from the associated cache and calculates the LOD value according to the hardware calculation logic of VK_KHR_compute_shader_derivatives. When the LOD value is less than 0, it is set to 0; when the LOD value is greater than the maximum Mipmap level, it is set to the maximum level. The texture ID, sampler ID, derivative result, LOD value, filtering mode, and number of Mipmaps are packaged into a sampling parameter structure and written to the Vulkan push constant. After the kernel intercepts the command containing texture sampling, it parses the sampling parameter structure in the push constant and writes the derivative result and LOD value to the GPU sampling unit hardware register according to the sampling unit register address mapping table.
[0040] Step 8: Update the parameter injection flag in the command buffer and resubmit the command buffer to the GPU queue; the GPU sampling unit uses the injected parameters to perform texture sampling, LOD calculation, and anisotropic filtering to complete the rendering.
[0041] To further reduce synchronization waiting time and improve execution efficiency, this invention adds a subgroup metadata tag containing derivative calculation mode, number of subgroups, number of threads, and synchronization type to the Vulkan command buffer when constructing it using VkSetCommandBufferTag; and adds a custom structure to the pNext chain of VkSubmitInfo2 when calling vkQueueSubmit, setting the task scheduling priority to VK_QUEUE_PRIORITY_HIGH.
[0042] Add the `derivative_subgroup_affinity` field to `drm_gpu_scheduler`. In Quads mode, subgroups are assigned to four consecutive GPU compute unit cores, while in Linear mode, they are assigned to two thread units within the same GPU compute unit. Update the association between CUs and subgroups. Add derivative task judgment logic to the `task_fit_cfs` function to prioritize scheduling them to CPU cores associated with the GPU compute unit, and set the `SCHED_FIFO` real-time scheduling class to avoid preemption by low-priority tasks.
[0043] In addition, within a set time before the execution of the Quads mode task, drm_gem_object_map is called to prefetch the shared memory blocks of the current and two adjacent subgroups into the GPU L1 cache to reduce the cache miss rate; SPIR-V instructions related to derivative calculation are decoded in advance and stored in the GPU instruction cache.
[0044] To address the forced derivative characteristics of shader models 6.6 and above, this invention achieves semantic lossless degradation. Specifically, it involves: constructing a high-version derivative semantic mapping table, including all derivative-related functions from shader models 6.6 to 6.8, and accurately mapping them to dual-mode simulation logic; mapping derivative array attributes to engine initialization instructions corresponding to the execution mode; modifying the HLSL abstract syntax tree to locate and replace high-version derivative function call nodes with simulation functions, and converting derivative array attributes to mode marker annotations; downgrading the shader model version to 6.5, retaining `-enable-66-features`, and adding the `-emulate-derivatives` attribute to disable native extension instructions; and adding a degradation marker to the D3D12 shader object to store metadata such as the number of replacement functions and the mapping table version.
[0045] Example:
[0046] This embodiment employs a hardware-assisted sampling-based Direct3D 12 derivative calculation implementation method provided by the present invention. It enables the normal operation of D3D12 applications in a GPU hardware environment that does not support the VK_KHR_compute_shader_derivatives extended features. The specific process includes:
[0047] The derivative feature extraction and classification of S1 and D3D12 computation shaders: A new D3D12 derivative feature parsing module is added to the shader module of VKD3D as a preprocessing step before HLSL shader compilation. The core is to extract all features related to VK_KHR_compute_shader_derivatives in the D3D12 computation shader to provide a basis for subsequent simulation.
[0048] S1.1, HLSL Syntax Tree Traversal: Using the Clang and LLVM HLSL front-end parser (version 16.0 and above, supporting Shader Model 6.6 syntax), traverse the Abstract Syntax Tree (AST) of the D3D12 computation shader to identify the following key elements:
[0049] Built-in functions related to derivatives: ddx, ddy, dwidth, dheight, ddx_coarse, ddy_fine (added in ShaderModel 6.6);
[0050] Texture sampling functions: tex2Dlod, tex3Dlod, tex2Dgather (sampling calls including derivative parameters);
[0051] Shader Model version identifier: such as [shader_model(6.6)] or [shader_model(6.7)];
[0052] Derivative group properties: [derivative_group(DerivativeGroupQuads)] or [derivative_group(DerivativeGroupLinear)] (new in Shader Model 6.6).
[0053] S1.2 Derivative Calculation Mode Marking: Based on the application scenario of the derivative function and the [derivative_group] attribute, mark its corresponding VK_KHR_compute_shader_derivatives execution mode. The marking rules are as follows:
[0054] If the [derivative_group(DerivativeGroupQuads)] attribute exists, or if the derivative function is used for 2D texture coordinate calculation (such as tex2Dlod(uv, ddx(uv))), or to process 2D mesh data (such as two-dimensional difference of particle positions), it is marked as Quads mode (corresponding to DerivativeGroupQuadsKHR).
[0055] If the [derivative_group(DerivativeGroupLinear)] attribute exists, or the derivative function is used for linear memory access (such as a structured buffer). <float>The index difference), or processing 1D data (such as linear interpolation difference of audio samples), is marked as Linear mode (corresponding to DerivativeGroupLinearKHR).
[0056] S1.3 Sampling and Derivative Association Mapping: Establish a three-dimensional association table between texture sampler ID, derivative function parameters, and sampling context to record the complete dependency relationship of each texture sampling operation. The table structure is shown below:
[0057] Table 1. Mapping Table of Sampling and Derivative
[0058] Texture Sampler ID Dependency derivative function Derivative parameter type Sampling function type Associated Subgroup ID sampler_0 ddx (uv), ddy (uv) float2 tex2Dlod subgroup_0 sampler_1 ddx (idx) int tex1Dlod subgroup_1
[0059] The associated subgroup ID is used in subsequent steps to locate the derivative calculation subgroup that the sampling operation depends on;
[0060] Feature data storage: The extracted list of derivative functions (including parameter types and call locations), execution mode markers, sampling and derivative association table, and Shader Model version information are stored in the extended fields of the VKD3D's D3D12Shader object (a new D3D12ShaderDerivativeFeatures structure) as core parameters for subsequent Vulkan pipeline construction.
[0061] S2. Construct a dual-mode adaptation layer for VK_KHR_compute_shader_derivatives. Add a derivative calculation mode adaptation layer to the SPIR-V code generation module of VKD3D. As the core link of HLSL to SPIR-V, it accurately replicates the dual execution mode semantics of VK_KHR_compute_shader_derivatives through three levels of logic: engine instantiation, instruction rewriting, and pipeline configuration.
[0062] S2.1 Execution mode detection and engine initialization: Read the feature data of D3D12ShaderDerivativeFeatures, call vkGetPhysicalDeviceFeatures2 to check if the GPU supports VK_KHR_compute_shader_derivatives. If not, initialize the two core engines of the dual-mode adaptation layer:
[0063] The Quads pattern engine, used to simulate DerivativeGroupQuadsKHR, includes the following components:
[0064] The thread index mapper defaults to a 2x2 quadruple thread index mapping rule. It calculates the relative ID within the subgroup using gl_LocalInvocationID, with the formula: subgroup_quad_id = (gl_LocalInvocationID.x % 2) + 2 * (gl_LocalInvocationID.y % 2), ensuring that each thread corresponds to a fixed position within the quadruple (0=(0,0), 1=(1,0), 2=(0,1), 3=(1,1)).
[0065] The shared memory allocator allocates a fixed-size shared memory block to each 2x2 subgroup (size = number of bytes of the derivative parameter type * 4, such as 8 bytes * 4 = 32 bytes for float2 type). The memory block address is aligned according to the product of the subgroup ID and the block size.
[0066] Synchronization instruction generator: Generates SPIR-V instructions for subgroupBarrier (when VK_KHR_shader_subgroup is supported) or memoryBarrierShared (when shared memory is only supported) based on GPU subgroup support;
[0067] A Linear pattern engine for simulating DerivativeGroupLinearKHR, comprising the following components:
[0068] The linear thread sorter predefines the index mapping rules for linear thread groups, subgroup_linear_id = gl_LocalInvocationID.x % 2, ensuring that threads are grouped in a linear order from 0 to 1, 2, 3, with 2 consecutive threads in each group;
[0069] Linear shared memory layout: Allocate a shared memory block for each linear subgroup (size = number of bytes of derivative parameter type * 2), and align the addresses according to the product of the subgroup ID and the block size;
[0070] Linear synchronization instruction generator: Generates synchronization instructions for linear threads, ensuring data visibility between adjacent threads.
[0071] S2.2, SPIR-V instruction rewriting (dual-mode differentiated implementation): The derivative function call of D3D12 is rewritten into a SPIR-V instruction sequence adapted to the native features of Vulkan, and processed separately according to the execution mode:
[0072] Instruction rewriting in Quads mode (taking ddx(f) as an example, where f is of type float2):
[0073] If the GPU supports VK_KHR_shader_subgroup and the subgroup size is not less than 4:
[0074] Command to generate subgroup memory bindings: OpDecorate %subgroupMemory SubgroupMemoryKHR;
[0075] Command to store thread results: OpStore %subgroupMemory %subgroup_quad_id %f (stores the value of f of the current thread in the corresponding location of the subgroup memory);
[0076] The command to generate a subgroup for synchronization is: OpSubgroupBarrierKHR;
[0077] Command to read adjacent thread results: %f_neighbor = OpLoad %subgroupMemory %subgroup_quad_id ^ 1 (Reads the f value of adjacent threads in the X direction, switching from 0 to 1 and from 2 to 3 by XORing with 1);
[0078] Generate the differential calculation command: %f_ddx = OpFSub %float2 %f_neighbor %f (calculate the derivative in the X direction);
[0079] If the GPU only supports shared memory:
[0080] Instructions for generating shared memory declarations: OpDecorate %sharedMemory WorkgroupMemory;
[0081] Instructions for generating shared memory address calculations: %shared_addr = OpIAdd %ptr_workgroup_float2%sharedMemory %subgroup_quad_id;
[0082] Instructions for generating thread result storage: OpStore %shared_addr %f;
[0083] Generate shared memory synchronization command: OpMemoryBarrier %MemoryBarrierShared;
[0084] Instructions for generating adjacent thread address calculations: %shared_addr_neighbor = OpIAdd %ptr_workgroup_float2 %sharedMemory %subgroup_quad_id ^ 1;
[0085] Command to generate adjacent thread result reading: %f_neighbor = OpLoad %ptr_workgroup_float2 %shared_addr_neighbor;
[0086] Generate the differential calculation command: %f_ddx = OpFSub %float2 %f_neighbor %f.
[0087] Instruction rewriting in Linear mode (taking ddx(idx) as an example, where idx is of type int):
[0088] Instructions for generating shared memory declarations: OpDecorate %linearSharedMemory WorkgroupMemory;
[0089] Generate linear address calculation instructions: %linear_addr = OpIAdd %ptr_workgroup_int %linearSharedMemory %subgroup_linear_id;
[0090] Instructions for generating thread result storage: OpStore %linear_addr %idx;
[0091] Generate shared memory synchronization command: OpMemoryBarrier %MemoryBarrierShared;
[0092] Generate the instruction to calculate the address of the next thread: %linear_addr_next = OpIAdd %ptr_workgroup_int %linearSharedMemory %subgroup_linear_id + 1;
[0093] Generate the command to read the results of the next thread: %idx_next = OpLoad %ptr_workgroup_int %linear_addr_next;
[0094] Generate the difference calculation instruction: %idx_ddx = OpISub %int %idx_next %idx (ddy is always 0 in linear mode, generating the OpConstant %int 0 instruction);
[0095] S2.3 Add a derivative parameter set to the pipeline layout. In the Vulkan compute pipeline layout VkPipelineLayout, add a derivative parameter descriptor set VkDescriptorSetLayout. This descriptor set contains the following bindings:
[0096] Binding 0: Unified buffer (UBO) for storing the association table of samples and derivatives, of type VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
[0097] Binding 1: Storage buffer (SSBO) for storing derivative calculation results, of type VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
[0098] Binding 2: Texture buffer (TBO) that stores GPU floating-point error compensation values, of type VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
[0099] By merging the descriptor set with the existing pipeline layout using vkCreatePipelineLayout, the GPU can quickly access the configuration parameters and intermediate results required for derivative calculations during execution.
[0100] S3 and Vulkan workgroups and subgroups are dynamically adapted and reorganized. A thread group reorganization module has been added to the compute shader module of VKD3D to replicate the core feature of 2x2 thread group collaboration of VK_KHR_compute_shader_derivatives, while adapting to the subgroup characteristics of different GPUs.
[0101] S3.1, the workgroup dimension splitting algorithm reads the execution mode flags of D3D12ShaderDerivativeFeatures, calculates the original workgroup dimension of the shader according to D3D12 (e.g., [16,16,1]), and dynamically splits it into a composite structure of basic dimension and subgroups. The splitting rules are as follows:
[0102] Quads pattern splitting:
[0103] Let the original working group dimension be [X, Y, Z], and the target subgroup dimension be [2, 2, 1].
[0104] Calculate the base dimensions: X_base = ceil(X / 2), Y_base = ceil(Y / 2), Z_base = Z;
[0105] If X is odd (e.g., X=17), then pad it with 1 to make it even (X=18) to ensure that each subgroup strictly contains 4 threads;
[0106] The final workgroup dimensions are [X_base, Y_base, Z_base]. Each base dimension has one 2x2 subgroup for each thread, and the total number of threads remains unchanged (e.g., [16,16,1] is split into [8,8,1], and the total number of threads = 8*8*1*4=256=16*16*1).
[0107] Linear pattern splitting:
[0108] Let the original working group dimension be [X, Y, Z], and the target subgroup dimension be [2, 1, 1].
[0109] Calculate the base dimensions: X_base = ceil(X / 2), Y_base = Y, Z_base = Z;
[0110] If X is odd (e.g., X=15), pad it with 1 to make it even (X=16), ensuring that each subgroup contains 2 threads;
[0111] The final workgroup dimension is [X_base, Y_base, Z_base], and the total number of threads = X_base*Y_base*Z_base*2 = X*Y*Z.
[0112] S3.2 Subgroup feature adaptation: The `vkGetPhysicalDeviceSubgroupProperties` function retrieves the GPU's subgroup features (subgroup size, supported subgroup operations, subgroup extension list) and dynamically selects the optimal thread collaboration method.
[0113] Quads pattern subgroup size adaptation:
[0114] If the subgroup size is 4 (e.g., Intel Arc A380): directly map the 2x2 subgroups to Vulkan subgroups, and use the split base dimension when calling vkCmdDispatch; If the subgroup size is 8 (e.g., AMD RX 580): divide the 8 threads into two 2x2 subgroups (IDs 0-3 in the subgroup are the first subgroup, and IDs 4-7 are the second subgroup), and achieve small-scale synchronization through shared memory within the subgroup; If the subgroup size is 32 (e.g., NVIDIA GTX 1060): divide the 32 threads into eight 2x2 subgroups, with each subgroup corresponding to an independent block of shared memory, avoiding cross-subgroup data interference.
[0115] Linear mode subgroup size adaptation:
[0116] For the Linear mode (target subgroup dimension [2,1,1], 2 consecutive threads per group), the thread grouping logic is dynamically adjusted according to the GPU subgroup size to ensure synchronization efficiency and data continuity in linear memory access scenarios:
[0117] Scenario 1: Subgroup size = 2 (e.g., Intel UHD 630, some AMD RDNA 1 architecture GPUs):
[0118] The target subgroup (2 consecutive threads) in Linear mode is directly mapped to a native Vulkan subgroup without additional splitting. The thread index mapping rule is: subgroup_linear_id = gl_LocalInvocationID.x % 2 (0 corresponds to the first thread in the subgroup, and 1 corresponds to the second thread). When calling vkCmdDispatch, the split base dimension is used (e.g., the original [16,1,1] is split into [8,1,1] base dimensions, and each base dimension thread corresponds to 1 native subgroup). During synchronization, OpSubgroupBarrierKHR is directly called (when VK_KHR_shader_subgroup is supported), without the need for shared memory overhead.
[0119] Scenario 2: Subgroup size = 4 (e.g., AMD RX 6600, NVIDIA RTX 3050): Split the 4 native subgroup threads into 2 independent Linear subgroups, each containing 2 consecutive threads. Specific splitting rules:
[0120] Calculate the linear offset of threads within a subgroup: `linear_offset = gl_LocalInvocationID.x % 4` (0-3); Assign a Linear subgroup ID: `linear_subgroup_id = linear_offset / 2` (0 corresponds to threads 0-1, 1 corresponds to threads 2-3); Determine the relative ID within a subgroup: `subgroup_linear_id = linear_offset % 2` (0 or 1, ensuring thread continuation); During synchronization, data visibility is achieved within the same Linear subgroup (e.g., threads 0-1) through `OpSubgroupBarrierKHR`, while different Linear subgroups (e.g., threads 0-1 and 2-3) do not require synchronization (linear memory access has no cross-subgroup dependency).
[0121] Scenario 3: Subgroup size = 8 (e.g., AMD RX 5700, NVIDIA GTX 1660): Split the 8 native subgroup threads into 4 Linear subgroups, with each subgroup maintaining a linear order of 2 consecutive threads to avoid data access fragmentation.
[0122] Thread index mapping: linear_offset = gl_LocalInvocationID.x % 8 (0-7); Linear subgroup ID allocation: linear_subgroup_id = linear_offset / 2 (0 corresponds to 0-1, 1 corresponds to 2-3, 2 corresponds to 4-5, 3 corresponds to 6-7); Shared memory optimization: If the GPU does not support subgroup expansion (no VK_KHR_shader_subgroup), allocate an independent shared memory block for each Linear subgroup (size = number of bytes of derivative parameter type * 2), block address = linear_subgroup_id * 2 * param_size, ensuring that threads only access shared memory within the same subgroup, reducing thread conflicts;
[0123] Scenario 4: Subgroup size = 32 (e.g., NVIDIA GTX 1080, AMD R9 Fury): For large subgroup scenarios, a multi-level splitting and continuous thread binding strategy is adopted to avoid cross-subgroup synchronization latency.
[0124] First-level splitting: Divide the 32 native subgroup threads into 4 blocks (0-7, 8-15, 16-23, 24-31) based on 8 thread blocks; Second-level splitting: Divide each 8-thread block into 4 Linear subgroups (each group consists of 2 consecutive threads); Thread identification: Record the global linear index using `subgroup_linear_global_id = gl_LocalInvocationID.x` to ensure that derivative calculations only access the results of consecutive threads within the same block (e.g., thread 0 only reads thread 1, thread 8 only reads thread 9); Synchronization instruction selection: If `VK_EXT_subgroup_size_control` is supported, temporarily adjust the subgroup size to 2 using `vkCmdSetSubgroupSize` to directly reuse native subgroup synchronization; if not supported, implement shared memory synchronization using `OpMemoryBarrier %MemoryBarrierShared` and limit the synchronization scope to within 8 thread blocks to reduce global synchronization overhead;
[0125] General adaptation rules:
[0126] Regardless of the GPU subgroup size, Linear mode must meet the core requirement of thread continuity within the subgroup to avoid linear memory access offsets caused by non-contiguous threads. Furthermore, the selection logic for synchronization instructions must be consistent with Quads mode, prioritizing subgroup instructions and then shared memory synchronization. This ensures that the thread collaboration semantics of derivative calculation in Linear mode are fully aligned with the DerivativeGroupLinearKHR execution mode of VK_KHR_compute_shader_derivatives.
[0127] S3.3 Shared memory layout optimization: For GPUs that only support shared memory (without VK_KHR_shader_subgroup), a 2x2 block aligned shared memory layout is designed.
[0128] Memory block partitioning: Divide the shared memory into multiple contiguous 2x2 sub-blocks. The size of each block = the maximum number of bytes of the derivative parameter type * 4 (if float4 type is supported, then it is 16 bytes * 4 = 64 bytes).
[0129] Address calculation optimization: The shared memory address calculation formula is shared_addr = (subgroup_id * 4 + quad_local_id) * param_size, where subgroup_id = gl_WorkGroupID.x * X_base + gl_WorkGroupID.y (X_base is the X value of the base dimension), and param_size is the number of bytes of the derivative parameter type. This ensures that the four threads in each subgroup access contiguous memory blocks, reducing bank conflicts.
[0130] Cache line alignment: Align the starting address of each sub-block to the GPU's cache line size (e.g., 64 bytes), achieved through the OpDecorate %sharedMemory Align 64 instruction, to improve the efficiency of shared memory access.
[0131] S4. Collaborative simulation and accuracy assurance of derivative functions: A dual-mode derivative calculation engine is implemented in the SPIR-V code generation module of VKD3D. Through the full-process logic of thread collaboration, synchronization, differentiation, and accuracy compensation, the derivative calculation accuracy and performance characteristics of VK_KHR_compute_shader_derivatives are replicated.
[0132] S4.1, Quads mode derivative calculation (taking 2D texture coordinates uv as an example: ddx, ddy):
[0133] Thread initialization and identity recognition:
[0134] Each thread calculates its own quad_local_id (0=(0,0), 1=(1,0), 2=(0,1), 3=(1,1)) using gl_LocalInvocationID; calculates subgroup_id (global subgroup unique identifier) using gl_WorkGroupID and the basic dimension; reads the binding 0 of the derivative parameter descriptor set (sampling and derivative association table) to confirm whether the uv parameter processed by the current thread is associated with texture sampling (e.g., associated with sampler_0).
[0135] Inter-thread data sharing and synchronization:
[0136] If VK_KHR_shader_subgroup is supported, the thread stores the current uv value (float2 type) in the quad_local_id position of the subgroup memory: OpStore %subgroupMemory %quad_local_id %uv, and calls OpSubgroupBarrierKHR to ensure that all threads complete the storage, avoiding race conditions during data reading; if only shared memory is supported, the shared memory address is calculated: shared_addr = (subgroup_id * 4 + quad_local_id) * 8 (float2 occupies 8 bytes), and the uv value is stored: OpStore %sharedMemory %shared_addr %uv, and calls OpMemoryBarrier %MemoryBarrierShared to ensure that the write operation of shared memory is visible to all threads.
[0137] Differential derivative calculation (by thread position):
[0138] Calculate ddx(uv) (X-direction derivative) and ddy(uv) (y-direction derivative) based on the different quad_local_id:
[0139] Thread 0 (0,0): ddx(uv), reads the uv value of thread 1 (1,0) into uv1, and calculates uv1 - uv0; ddy(uv), reads the uv value of thread 2 (0,1) into uv2, and calculates uv2 - uv0;
[0140] Thread 1 (1,0): ddx(uv): Reads the uv value of thread 0 (0,0) into uv0, and calculates uv0 - uv1; ddy(uv): Reads the uv value of thread 3 (1,1) into uv3, and calculates uv3 - uv1;
[0141] Thread 2 (0,1): ddx(uv): Reads the uv value of thread 3 (1,1) into uv3, and calculates uv3 - uv2; ddy(uv): Reads the uv value of thread 0 (0,0) into uv0, and calculates uv0 - uv2;
[0142] Thread 3 (1,1): ddx(uv): Reads the uv value of thread 2 (0,1) into uv2, and calculates uv2 - uv3; ddy(uv): Reads the uv value of thread 1 (1,0) into uv1, and calculates uv1 - uv3;
[0143] Example of a calculation instruction (ddx for thread 0): %uv1 = OpLoad %subgroupMemory 1, %uv_ddx =OpFSub %float2 %uv1 %uv0.
[0144] Precision compensation mechanism:
[0145] To address the floating-point operation errors of different GPU architectures, differentiated compensation is implemented: An error compensation table for each GPU architecture is pre-built: Error values for different architectures are recorded through offline testing (using GPUs that support VK_KHR_compute_shader_derivatives as a reference), as shown in the table below:
[0146] Table 2 GPU Architecture Error Compensation Table
[0147] GPU architecture ddx (uv) compensation value ddy (uv) compensation value NVIDIA Maxwell 1.2e-6 1.1e-6 AMD GCN 5.3e-7 5.1e-7 Intel Gen9 8.5e-7 8.3e-7
[0148] Read the derivative parameter descriptor set binding 2 (error compensation TBO) to obtain the current GPU compensation value; generate the compensation calculation instruction: %uv_ddx_compensated = OpFAdd %float2 %uv_ddx %compensation_ddx;
[0149] Derivative result storage: Store the compensated uv_ddx and uv_ddy in the derivative parameter descriptor set binding 1 (SSBO), storage address = subgroup_id * 16 + quad_local_id * 4 (each derivative result occupies 4 bytes, float2 occupies 8 bytes);
[0150] S4.2, Linear Mode Derivative Calculation (Taking the structured buffer index idx as an example, ddx):
[0151] Thread initialization:
[0152] The thread calculates the subgroup_linear_id (0 or 1);
[0153] Calculate the shared memory address: linear_shared_addr = subgroup_id * 8 + subgroup_linear_id * 4 (int occupies 4 bytes);
[0154] Data sharing and synchronization:
[0155] Store the idx value: OpStore %linearSharedMemory %linear_shared_addr %idx;
[0156] Call OpMemoryBarrier %MemoryBarrierShared;
[0157] Derivative calculation and compensation:
[0158] Thread 0: Reads the idx value from thread 1 into idx1, and calculates idx_ddx = idx1 - idx0;
[0159] Thread 1: Reads the idx value from thread 0 into idx0, and calculates idx_ddx = idx0 - idx1;
[0160] Precision compensation: Read the error compensation table and add the corresponding integer error compensation value for the architecture (e.g., 1 for NVIDIA Maxwell and 0 for AMD GCN).
[0161] Result storage: Store idx_ddx in SSBO, address = subgroup_id * 4 + subgroup_linear_id * 2;
[0162] S4.3, Derivative Calculation Integrity Verification: A new verification command has been added to ensure the correctness of derivative calculations.
[0163] Generate a subgroup summation command (Quads mode): %ddx_sum = OpSubgroupReduceAddKHR%float2 %uv_ddx_compensated; If the absolute value of the summation exceeds the threshold (e.g., 1e-4), it is marked as a calculation exception, and the subsequent pipeline verification module will handle the exception.
[0164] S5, a hardware-assisted simulation of texture sampling to derivative binding, replicates the integrated derivative and sampling feature of VK_KHR_compute_shader_derivatives. Through application-layer caching and kernel-layer command injection, it realizes the hardware-level transfer of derivative results to the GPU sampling unit, avoiding the precision loss of manual LOD calculation.
[0165] S5.1 and VKD3D: Derivative and sampling buffer construction and parameter packaging:
[0166] Derivative and sampling associated buffer initialization:
[0167] A new derivative and sampling associated cache has been added to the texture processing module. This cache is a hash table structure, with the key = texture ID + sampler ID + subgroup ID, and the value = derivative result (dudx, dudy, dvdx, dvdy) + LOD pre-calculated value.
[0168] Sampling proxy function implementation:
[0169] Create a sampling proxy function, VKD3D_tex_sample_emulate, to replace the native sampling functions of D3D12 (such as the texture sampling calls in ID3D12GraphicsCommandList::DrawIndexedInstanced). The function logic is as follows:
[0170] Input parameters: texture ID, sampler ID, subgroup ID, texture coordinates (uv);
[0171] Cache lookup: Read derivative results from the associated cache using key texture ID, sampler ID, and subgroup ID (dudx=ddx(uv).x, dudy=ddy(uv).x, dvdx=ddx(uv).y, dvdy=ddy(uv).y);
[0172] LOD pre-computation: Calculate the LOD value according to the hardware computation logic of VK_KHR_compute_shader_derivatives: LOD = 0.5 * log2( (dudx*dudx + dvdx*dvdx) + (dudy*dudy + dvdy*dvdy) ); if LOD < 0, set it to 0; if LOD > maximum mipmap level, set it to the maximum level.
[0173] Parameter Packing: Pack the following parameters into a sampling parameter structure (size = 64 bytes, aligned to the GPU cache line):
[0174] struct SamplerDerivativeParams {
[0175] uint32_t texture_id; / / Texture ID
[0176] uint32_t sampler_id; / / Sampler ID
[0177] float dudx, dudy, dvdx, dvdy; / / Derivative results
[0178] float lod; / / Pre-calculated LOD value
[0179] uint32_t filter_mode; / / Filtering mode (e.g., anisotropy)
[0180] uint32_t mip_count; / / Number of mipmaps
[0181] };
[0182] Push constant writing: Write the sampling parameter structure to Vulkan's push constant. The offset address of the push constant = sampler_id * 64 (to ensure that the parameters of each sampler are stored independently), and the offset address is aligned to 64 bytes.
[0183] S5.2, Linux kernel side, Vulkan command interception and parameter injection.
[0184] Command interceptor initialization:
[0185] Modify the Vulkan command queue management module in the Linux kernel to add a Vulkan sampling command interceptor. This interceptor is registered to the pre-submit hook of the Vulkan command queue and intercepts the following commands that contain texture sampling:
[0186] vkCmdDrawIndexed(indexed drawing, including vertex shader texture sampling);
[0187] vkCmdDispatch (Computation shader, including computation shader texture sampling);
[0188] vkCmdDraw (non-indexed drawing, including vertex shader texture sampling);
[0189] Push constant parsing:
[0190] When the above command is intercepted, the following parsing logic is executed:
[0191] Get the physical address of the push constant region of the command buffer using vkGetCommandBufferInfo (get the physical address of the GEM object using drm_gem_object_get); traverse the push constant region by 64-byte offset, identify the sampling parameter structure (judged by the non-zero values of texture_id and sampler_id); extract the dudx / dudy / dvdx / dvdy and lod values, as well as filter_mode and mip_count from the structure;
[0192] GPU register mapping and parameter injection: Based on the GPU manufacturer's register definitions, the parsed parameters are injected into the hardware registers of the sampling unit.
[0193] NVIDIA GPU:
[0194] Get the TEX_UNIT corresponding to the sampler (e.g., sampler_0 corresponds to TEX_UNIT_0);
[0195] Write the LOD value to bits [31:0] of the TEX_LOD_CTRL_0 register (address = 0x1200 + TEX_UNIT*0x100);
[0196] Write dudx or dvdx to the TEX_DERIV_X_CTRL register (address = 0x1204 + TEX_UNIT*0x100);
[0197] Write dudy or dvdy into the TEX_DERIV_Y_CTRL register (address = 0x1208 + TEX_UNIT*0x100);
[0198] AMD GPU:
[0199] Get the SAMPLER_ID corresponding to the sampler (e.g., sampler_0 corresponds to SAMPLER_0).
[0200] Write the LOD value to bits [15:0] of the SAMPLER_LOD_PARAMS register (address = 0x2000 + SAMPLER_ID*0x20);
[0201] Write dudx and dudy or dvdx and dvdy into the SAMPLER_DERIV_PARAMS register (address = 0x2004 + SAMPLER_ID * 0x20).
[0202] Intel GPU:
[0203] Write the LOD value to the GEN_SAMPLER_LOD register (address = 0x3000 + sampler_id * 0x18).
[0204] Write the derivative result to the GEN_SAMPLER_DERIV register (address = 0x3008 + sampler_id*0x18).
[0205] Command resubmitted:
[0206] After register injection is completed, the parameter injection mark is updated in the command buffer. Then, drm_sched_job_submit is called to resubmit the command to the GPU queue. This ensures that when the sampling unit performs texture sampling, it directly uses the injected derivative results to calculate LOD and filter weights, completely replicating the hardware-level sampling logic of VK_KHR_compute_shader_derivatives.
[0207] S6 and Linux kernel derivative subgroup affinity scheduling optimizations optimize the scheduling strategy for GPU tasks to meet the 2x2 thread group requirements of derivative simulation, reduce synchronization waiting time, and improve execution efficiency.
[0208] S6.1 Subgrouping and Metadata Attachment for GPU Tasks: Modify the D3D 12 command queue management module of VKD3D to attach subgroup metadata for tasks involving derivative calculations when building the Vulkan command buffer.
[0209] Subgroup ID tag:
[0210] Add a tag to the command buffer using `vkSetCommandBufferTag`, with tag type `VK_COMMAND_BUFFER_TAG_TYPE_DERIVATIVE_SUBGROUP`, and tag data including:
[0211] Derivative calculation mode (Quads / Linear, 1 byte);
[0212] Number of subgroups (4 bytes);
[0213] Number of threads per subgroup (1 byte, Quads=4, Linear=2).
[0214] Synchronization type within a subgroup (subgroup instruction / shared memory, 1 byte);
[0215] Scheduling priority settings:
[0216] When calling vkQueueSubmit, add a custom VkDerivativeSubgroupPriorityInfo structure to the pNext chain of VkSubmitInfo2 and set the task's scheduling priority to VK_QUEUE_PRIORITY_HIGH (higher than normal rendering tasks).
[0217] S6.2 Kernel scheduling policy modification (dual scheduler collaborative optimization): Modify the Linux kernel's CPU CFS scheduler and GPU scheduler, and add a derivative subgroup affinity policy:
[0218] GPU scheduler subgroup affinity assignment:
[0219] Add a new field, derivative_subgroup_affinity, to drm_gpu_scheduler to record the subgroup ID currently associated with each GPU compute unit (CU);
[0220] When the scheduler receives a task with subgroup metadata, it executes the following allocation logic:
[0221] If it is in Quads mode (4 threads or subgroups): the subgroups are assigned to 4 consecutive CU cores (such as CU0-CU3), ensuring that threads within the subgroup can share data through the fast channel between CUs (such as AMD's Infinity Fabric, NVIDIA's NVLink);
[0222] If in Linear mode (2 threads or subgroups): the subgroups are assigned to 2 thread units within the same CU to avoid latency in cross-CU scheduling;
[0223] Update `derivative_subgroup_affinity` to mark the association between the CU and the subgroup, and subsequent tasks in the same subgroup will be preferentially assigned to the same CU;
[0224] Coordination of the CPU scheduler:
[0225] In the task_fit_cfs function of the CPU CFS scheduler, a new derivative task judgment logic has been added: if the task is a derivative calculation thread of VKD3D (judged by process name and task ID), it will be scheduled to the CPU core associated with the GPU CU first (e.g., GPU CU0 corresponds to CPU cores 0-3).
[0226] Set the SCHED_FIFO scheduling class (real-time scheduling) for the derivative calculation thread to avoid being preempted by low-priority tasks and ensure that the GPU commands submitted by the CPU match the GPU execution rhythm.
[0227] S6.3, Optimization of cache prefetching and latency hiding:
[0228] Shared memory prefetching:
[0229] Modify the Linux kernel's GPU DRM cache management module to add 2x2 block prefetch logic:
[0230] When the GPU scheduler receives a Quads mode task, it calls drm_gem_object_map to prefetch the shared memory block of the task into the GPU L1 cache 100 microseconds before the task is executed (the delay is set by drm_timer_add);
[0231] The prefetch range is equal to the shared memory block of the current subgroup plus the shared memory blocks of the two adjacent subgroups, thus reducing the cache miss rate.
[0232] Instruction pre-decoding:
[0233] Add derivative instruction pre-decoding logic to drm_gpu_scheduler to pre-decode SPIR-V instructions related to derivative calculation (such as OpFSub / OpLoad) and store them in the GPU instruction cache (ICache) to avoid instruction decoding delays affecting thread synchronization efficiency.
[0234] S6.4, Monitoring and Dynamic Adjustment of Scheduling Effects:
[0235] Add a derivative subgroup scheduling statistic to the kernel's GPU scheduling statistics module to monitor the following metrics:
[0236] The difference in thread startup latency within a subgroup (the difference between the maximum and minimum values, with a target of less than 1 microsecond).
[0237] Shared memory cache hit rate (target > 95%);
[0238] Subgroup synchronization waiting time (target less than 50 nanoseconds);
[0239] If the target is not met, the prefetch range or CU allocation strategy will be dynamically adjusted (e.g., increasing the number of prefetch blocks or adjusting CU associations).
[0240] S7 and Shader Model 6.6 and above semantic lossless degradation and pipeline verification: Taking advantage of the forced derivative feature of Shader Model 6.6 and above, lossless degradation is achieved through a three-level logic of semantic mapping, syntax tree modification and pipeline verification to ensure the functional integrity of high version shaders.
[0241] Derivative semantic mapping table construction for S7.1 and Shader Model 6.6 and above:
[0242] In VKD3D's SPIR-V code generation module, a semantic mapping table for high-version derivative functions is constructed, covering all derivative-related functions in ShaderModel 6.6-6.8. The mapping rules are based on the semantic definition of VK_KHR_compute_shader_derivatives, and the table structure is as follows:
[0243] Table 3. Derivative semantic mapping table for Shader Model 6.6 and above
[0244] Shader Model 6.6 + Functions Mapping target Additional processing logic ddx_coarse(f) Quads mode: The average of the DDX results of the 4 threads within the subgroup. After summing by calling OpSubgroupReduceAddKHR, divide by 4 ddy_fine(f) Quads mode: The ddy results of adjacent threads are directly differed. Disable precision compensation (fine mode requires low latency). dwidth(f) Quads mode: Returns the number of threads in the subgroup (fixed to 4); Linear mode: Returns 2. Generate an OpConstant instruction with the value equal to the number of threads in the subgroup. dheight(f) Quads mode: Returns the number of threads in the y-direction within the subgroup (fixed to 2); Linear mode: Returns 1. Generates an OpConstant directive with a value of 2 or 1. derivative_group_barrier() Quads mode: OpSubgroupBarrierKHR; Shared memory: OpMemoryBarrier Dynamically select based on GPU subgroup support. tex2Dlod_coarse(s, uv) Call vkd3d_tex_sample_emulate and use the results of ddx_coarse / ddy_coarse. LOD is calculated using the average value.
[0245] [derivative_group] property mapping: Maps the [derivative_group(DerivativeGroupQuads)] property of Shader Model 6.6 to Quads mode engine initialization instructions, and [derivative_group(DerivativeGroupLinear)] to Linear mode engine initialization instructions;
[0246] S7.2 Shader Downgrade Compilation (Semantic Lossless Modification): In the d3d12_compile_shader function of the VKD3D shader module, a downgrade compilation branch is added, and the following modifications are performed for shaders with Shader Model 6.6 and above:
[0247] Syntax tree modification (based on Clang AST):
[0248] Traverse the HLSL AST to locate all higher version derivative function call nodes (such as ddx_coarse).
[0249] Based on the mapping table, replace the function call node with the corresponding simulated function call (such as VKD3D_ddx_coarse_emulate).
[0250] Locate the [derivative_group] attribute node and replace it with a pattern tag annotation for the adapter layer to recognize;
[0251] If the shader uses WaveIntrinsics and depends on derivatives, add logic linking wave synchronization and derivative calculation (e.g., calling the simulated derivative function after waveBarrier()).
[0252] Compiler options adjustment:
[0253] Change the target version of the HLSL compiler from 6.6 to 6.5, but retain the -enable-66-features option (to ensure that other 6.6 features such as SamplerFeedback are still available).
[0254] Add the `-emulate-derivatives` compilation option to tell the compiler to generate SPIR-V code adapted to the simulation logic (e.g., avoid using the extended directives of `VK_KHR_compute_shader_derivatives`).
[0255] Degradation tags and metadata storage:
[0256] Add a degradation flag (is_derivative_emulated = true) to the compiled D3D12Shader object and store metadata about the degradation process (such as the number of functions replaced and the mapping table version) for subsequent pipeline verification.
[0257] S7.3 Pipeline Validation and Anomaly Handling (Multi-level Validation): A derivative simulation pipeline validation module is added to the VKD3D rendering pipeline module to perform three-level validation on the constructed Vulkan computation pipeline:
[0258] Static verification (at compile time):
[0259] Verify that there are no extended instructions of VK_KHR_compute_shader_derivatives in the SPIR-V code (such as OpDerivativeGroupQuadsKHR); verify that the access logic of shared memory or subgroup memory matches the execution mode (such as 2x2 block access in Quads mode); verify that the precision compensation instructions are correctly generated (such as OpFAdd compensation value).
[0260] Dynamic verification (on first execution):
[0261] Select a reference scenario (such as solid color texture sampling, simple mesh derivative calculation), and perform the following comparison:
[0262] Execute the same shader on a GPU that supports VK_KHR_compute_shader_derivatives and obtain the reference derivative result; execute the simulation logic on the current GPU and obtain the simulation derivative result; calculate the deviation rate between the two: deviation rate = |simulated value - reference value| / |reference value|; if the deviation rate is ≤1% (normal scene) or ≤0.5% (texture sampling scene), the verification passes; otherwise, it is marked as the accuracy is not up to standard.
[0263] Runtime verification (per frame sampling):
[0264] Ten derivative calculation results are randomly sampled every 100 frames and compared with the reference value. If the deviation rate exceeds the threshold for three consecutive frames, dynamic precision adjustment is triggered (such as increasing the compensation value or switching the synchronization mode). If the result is still not up to standard after adjustment, a precision prompt is output to the user (such as the current scene derivative precision is low, and it is recommended to reduce the image quality settings).
[0265] Abnormal rollback mechanism:
[0266] If a serious error is detected during verification (such as shared memory access out of bounds or register injection failure), the system will automatically roll back to compatibility mode: disable subgroup affinity scheduling; use double-precision calculation (float64) to improve derivative precision; reduce the texture filtering level (e.g., from anisotropic filtering to bilinear filtering); and record the rollback reason and logs for developers to debug.
[0267] S8, Derivative Calculation Result Feedback and D3D12 Interface Adaptation: This section describes the result feedback process of the D3D12 to Vulkan conversion, ensuring that the simulated derivative calculation results are correctly fed back to the D3D12 application, thus resolving the oversight of result gaps in the existing solution.
[0268] S8.1 Derivative Result Buffer Mapping and Reading:
[0269] Buffer mapping:
[0270] In the ExecuteCommandLists function of the D3D 12 command queue management module of VKD3D, after the command buffer is completed, vkMapMemory is called to map and store the SSBO of the derivative result, with the mapping type being VK_MEMORY_MAP_FLAGS_READ_BIT.
[0271] Result reading and format conversion:
[0272] Read the derivative results in SSBO in order from subgroup ID to thread ID (such as uv_ddx and uv_ddy in Quads mode); convert the SPIR-V floating-point format (such as float2) to the corresponding D3D12 format (such as D3D12_FLOAT2); handle calculation exception flags, and if there are abnormal results, use the results of the previous frame and smooth interpolation to replace them.
[0273] S8.2 and D3D12 interface result population:
[0274] Based on the call type of the D3D12 application, populate the derivative results into the corresponding D3D12 interface structure:
[0275] Calculate shader results and return them:
[0276] If the application calls ID3D12GraphicsCommandList::Dispatch to execute the compute shader and read the result, write the derivative result to the corresponding offset in ID3D12Resource (structured buffer or constant buffer); call ID3D12CommandQueue::Signal to notify the application that the result is ready.
[0277] Rendering pipeline results return:
[0278] If the derivative result is used in a vertex shader or pixel shader (such as vertex position adjustment or pixel lighting calculation), fill the result into the corresponding field of D3D12_VERTEX_BUFFER_VIEW / D3D12_CONSTANT_BUFFER_VIEW; ensure that the format of the returned result is consistent with the API definition of D3D12 (such as matching the format of D3D12_SHADER_RESOURCE_VIEW_DESC).
[0279] S8.3 Result Consistency Verification and Log Recording:
[0280] Consistency check:
[0281] Compare the returned result with the reference derivative result to ensure that the deviation rate is no greater than 1%; verify the return code of the D3D12 interface (such as S_OK), and return S_FALSE with an additional derivative simulation warning if the result is abnormal;
[0282] Log records:
[0283] Record the derivatives of each frame back to the original calculation (such as the number of returned results, the number of anomalies, and the deviation rate); if the application is in debug mode, output a detailed result comparison log (reference value, simulated value, deviation) to help developers locate problems.
[0284] S8.4 Interface compatibility guarantee:
[0285] Ensure that the return logic is transparent to D3D12 applications (applications do not need to modify their code); support D3D12 result query interfaces (such as ID3D12QueryHeap) to query performance data of derivative calculation (such as execution time, number of synchronizations); if the application calls ID3D12Device::CheckFeatureSupport to query D3D12_FEATURE_DERIVATIVE_GROUP, return support (SUPPORTED) and attach the simulation implementation flag (D3D12_DERIVATIVE_GROUP_SUPPORT_EMULATED).
[0286] Experiments have verified that this invention, by deeply replicating the dual execution mode and hardware-level features of VK_KHR_compute_shader_derivatives, combined with collaborative optimization and result feedback adaptation at the application and kernel layers, achieves the following technical effects:
[0287] Significantly improved compatibility, supporting over 95% of older GPUs that do not have VK_KHR_compute_shader_derivatives (such as NVIDIA GTX 10 series, AMD RX 500 series, Intel UHD 630), reducing the rendering error rate of D3D12 games from 80% to below 3%, and achieving 98% compatibility with Shader Model 6.6 and above (including advanced features such as FSR 4.0 and ray tracing noise reduction);
[0288] Significant performance optimizations have been achieved. Through subgroup affinity scheduling, cache prefetching, and hardware parameter injection, the performance of derivative calculation is improved by 50% to 70% compared to traditional pure software simulation, approaching the performance of scenarios that support VK_KHR_compute_shader_derivatives in hardware (only within 8%). The frame rate loss in AAA games has been reduced from 30% to below 8%.
[0289] With high accuracy reproduction, through GPU architecture-differentiated accuracy compensation and hardware-level derivative-sampling binding, the LOD calculation accuracy of texture sampling reaches more than 99% of the hardware-supported scenes, completely solving problems such as texture flicker, mipmap level error, and anisotropic filtering failure.
[0290] The process is complete, and the addition of derivative result feedback fills the gaps in the existing solution's process, ensuring that the return value of the D3D12 API call is completely consistent with the hardware-supported scenarios, and the application can run normally without modification;
[0291] It is highly maintainable, with the dual-mode adaptation layer and semantic mapping table decoupled from the existing code. Subsequent updates to the VK_KHR_compute_shader_derivatives functionality (such as adding execution modes) can directly reuse the framework of this solution without large-scale refactoring.
[0292] With no hardware driver dependencies, all modifications only involve the VKD3D application layer and the Linux kernel layer (DRM subsystem), without requiring modifications to the GPU chip's proprietary driver. This results in low adaptation costs and allows for rapid rollout to mainstream Linux distributions.
[0293] This invention solves the compatibility, performance, accuracy, and workflow integrity issues of VKD3D on older GPUs. It is not only applicable to VKD3D projects, but can also be extended to the development of other D3D12 to Vulkan compatibility layers (such as DXVK-Next). It provides a low-cost, efficient, and high-precision solution for upgrading the graphics API of older hardware, helping to expand the game ecosystem on the Linux platform and extend the life cycle of older devices.
[0294] In summary, the above are merely preferred embodiments of the present invention and are not intended to limit the scope of protection of the present invention. Any modifications, equivalent substitutions, improvements, etc., made within the spirit and principles of the present invention should be included within the scope of protection of the present invention.< / float>
Claims
1. A method for calculating Direct3D 12 derivatives based on hardware-assisted sampling, characterized in that, Specifically, the following steps are included: After VKD3D and D3D applications start, when the application creates a compute shader, VKD3D traverses the abstract syntax tree of the D3D12 compute shader, sets the execution mode flag of the obtained derivative function VK_KHR_compute_shader_derivatives, and establishes the first association table between the texture sampler, derivative function parameters, and sampling context. When the execution mode is marked as Quads mode, the DerivativeGroupQuadsKHR is simulated to construct the SPIR-V instruction sequence based on whether the GPU supports subgroup operations and the subgroup size; when the execution mode is marked as Linear mode, the DerivativeGroupLinearKHR is simulated to construct the SPIR-V instruction sequence; the derivative function of D3D12 is rewritten using the SPIR-V instruction sequence to obtain the derivative simulation logic and the Vulkan shader module; Obtain the original workgroup dimension of the D3D12 compute shader. Determine the target subgroup dimension and subgroup thread count by the execution mode flag. The original workgroup dimension becomes a workgroup dimension that includes the base dimension and the subgroup, satisfying that the product of each dimension of the workgroup and the product of the subgroup thread count are equal to the product of the original workgroup dimension after pairing. Divide the current subgroup according to the characteristics of the GPU subgroup and map it to a Vulkan subgroup, sharing the memory layout configuration. In the Vulkan compute pipeline layout, a set of derivative parameter descriptors is merged, including a unified buffer containing the first association table, a storage buffer containing the derivative calculation results, and a texture buffer containing the GPU floating-point error compensation values. This results in the Vulkan pipeline layout. A Vulkan compute pipeline containing derivative simulation logic is created and bound to the command buffer. When the application records a texture sampling command, the native sampling function is modified into a sampling proxy function. The command buffer is submitted to the GPU queue. The GPU calculates the derivative results using the derivative simulation logic and writes them to the storage buffer. The sampling proxy function calculates the LOD value based on the derivative results, constructs the sampling parameter structure, and writes it to the Vulkan push constant, which is then written to the GPU sampling unit hardware register by the kernel. The command buffer is then resubmitted to the GPU queue, and the GPU sampling unit completes the rendering.
2. The Direct3D 12 derivative calculation method according to claim 1, characterized in that, The method for marking the execution mode of the VK_KHR_compute_shader_derivatives of the acquired derivative function is as follows: if the derivative array attribute DerivativeGroupQuads exists, or the derivative function is used for 2D texture coordinate calculation or processing 2D mesh data, then the derivative function is marked as Quads mode; if the derivative array attribute DerivativeGroupLinear exists, or the derivative function is used for linear memory access or processing 1D data, then the derivative function is marked as Linear mode.
3. The Direct3D 12 derivative calculation implementation method according to claim 1, characterized in that, The method by which the simulated DerivativeGroupQuadsKHR constructs the SPIR-V instruction sequence based on whether the GPU supports subgroup operations and the subgroup size is as follows: The thread position is calculated using gl_LocalInvocationID.x%2 and gl_LocalInvocationID.y%2, which identify the fixed position of the thread within the subgroup. gl_LocalInvocationID.x is the X-axis index of the thread within the workgroup, and gl_LocalInvocationID.y is the Y-axis index of the thread within the workgroup. Shared memory blocks are allocated to the subgroup, and subgroup synchronization instructions are generated based on the GPU hardware capabilities. When the GPU supports subgroup operations and the subgroup size is not less than a set value, a SPIR-V instruction sequence consisting of subgroup memory binding, thread result storage, subgroup synchronization, adjacent thread result reading, and differential calculation is constructed. When the GPU only supports shared memory, a SPIR-V instruction sequence consisting of shared memory declaration, shared memory address calculation, thread result storage, shared memory synchronization, adjacent thread address calculation, adjacent thread result reading, and differential calculation is constructed.
4. The Direct3D 12 derivative calculation implementation method according to claim 1, characterized in that, The method for constructing the SPIR-V instruction sequence using the simulated DerivativeGroupLinearKHR is as follows: The thread position is calculated using gl_LocalInvocationID.x%2, identifying the thread's position within the linear subgroup; a linearly contiguous shared memory layout is allocated to the subgroup, and synchronization instructions adapted to the linear grouping are generated; a SPIR-V instruction sequence is constructed, consisting of shared memory declaration, linear address calculation, thread result storage, shared memory synchronization, next thread address calculation, next thread result reading, and differential calculation, where gl_LocalInvocationID.x is the X-axis index of the thread within the workgroup.
5. The Direct3D 12 derivative calculation implementation method according to claim 1, characterized in that, The original workgroup dimension is transformed into a workgroup dimension that includes a base dimension and subgroups in the following way: the base dimension is calculated by rounding up and dividing by the length of the corresponding dimension of the subgroup, and the other dimensions remain consistent with the original workgroup. For the dimensions that need to be split, a parity check is performed. If the number is odd, 1 is added to make it even.
6. The Direct3D 12 derivative calculation implementation method according to claim 1, characterized in that, The method of dividing the current subgroup according to the characteristics of the GPU subgroup and mapping it to the Vulkan subgroup is as follows: For Quads mode, the division method of the current subgroup is determined according to the size of the GPU subgroup and then the resulting subgroup is mapped to the Vulkan subgroup. For Linear mode, the grouping method is dynamically determined according to the size of the GPU subgroup under the condition of thread continuity within the subgroup and then the resulting subgroup is mapped to the Vulkan subgroup.
7. The Direct3D 12 derivative calculation implementation method according to claim 6, characterized in that, The method for determining the division of the current subgroup based on the GPU subgroup size and then mapping the resulting subgroups to Vulkan subgroups in Quads mode is as follows: when the GPU subgroup size is 4, the current subgroup is mapped to a Vulkan subgroup; when the GPU subgroup size is 8, the 8 threads are divided into two 2x2 subgroups and then mapped to Vulkan subgroups, with synchronization achieved through shared memory within the subgroups; when the GPU subgroup size is 32, the 32 threads are divided into eight 2x2 subgroups and then mapped to Vulkan subgroups, with each subgroup corresponding to an independent block of shared memory.
8. The Direct3D 12 derivative calculation implementation method according to claim 6, characterized in that, For Linear mode, the grouping method is dynamically determined based on the GPU subgroup size while ensuring thread continuity within the subgroup. Specifically: when the GPU subgroup size is 2, the current subgroup is mapped to a Vulkan subgroup; when the GPU subgroup size is 4, the 4 current subgroup threads are split into 2 independent Linear subgroups, each containing 2 consecutive threads; when the GPU subgroup size is 8, the 8 current subgroup threads are split into 4 Linear subgroups, each maintaining a linear order of 2 consecutive threads; when the GPU subgroup size is 32, the 32 current subgroup threads are first divided into 4 thread blocks of 8 threads each, and then each thread block is further divided into 4 Linear subgroups of 2 consecutive threads each.
9. The Direct3D 12 derivative calculation method according to claim 8, characterized in that, In Linear mode, when the GPU subgroup size is 2, the thread index mapping rule within the subgroup is subgroup_linear_id = gl_LocalInvocationID.x%2, where subgroup_linear_id is the thread index of the subgroup, and gl_LocalInvocationID.x is the X-axis index of the thread within the workgroup. When the GPU subgroup size is 4, the linear offset of the thread within the subgroup is calculated as gl_LocalInvocationID.x%4, the Linear subgroup ID is assigned as linear_offset / 2, and the relative ID within the subgroup is determined as linear_offset%2. When the GPU subgroup size is 8, the linear offset of the thread within the subgroup is calculated as gl_LocalInvocationID.x%8, and the Linear subgroup ID is assigned as linear_offset / 2.
10. The Direct3D 12 derivative calculation method according to claim 1, characterized in that, The GPU calculates the derivative result through derivative simulation logic as follows: the thread calculates the position and subgroup identifier within its subgroup, and reads the first association table in the derivative parameter descriptor set to confirm that the parameter to be processed is associated with the texture sampling. Each thread stores thread data through subgroup memory and shared memory, and calls synchronization instructions to ensure data visibility. Based on the thread's position within the subgroup, read data from adjacent threads to calculate the X-direction and Y-direction derivatives; read the error compensation value from the texture buffer of the derivative parameter descriptor set to correct floating-point operation errors and obtain the derivative results.