Direct3D 12 descriptor compatible method based on address homomorphism mapping
By creating a virtual descriptor heap and mapping table when the GPU does not support VK_EXT_push_descriptor, the compatibility issues of Direct3D 12 applications on the Linux platform are resolved, and the normal operation and stability of D3D 12 applications are achieved.
Patent Information
- Authority / Receiving Office
- CN · China
- Patent Type
- Patents(China)
- Current Assignee / Owner
- 北京麟卓信息科技有限公司
- Filing Date
- 2026-02-25
- Publication Date
- 2026-06-19
AI Technical Summary
Without GPU support for the VK_EXT_push_descriptor feature, the VKD3D translation layer struggles to achieve efficient and stable operation of Direct3D 12 applications on the Linux platform, leading to compatibility issues.
By creating a virtual descriptor heap to achieve source mapping between CPU and GPU addresses, initializing the descriptor push packet cache queue, mapping table, and layout fingerprint hash table, intercepting root signature calls and converting them into Vulkan pipeline layouts, generating descriptor push packets, batch merging and submitting Vulkan commands, the adaptation of D3D12 semantics to Vulkan is achieved.
Even without GPU support for the VK_EXT_push_descriptor feature, we achieved normal operation of Direct3D 12 applications on the Linux platform, resolving compatibility issues and ensuring application stability and efficiency.
Smart Images

Figure CN121722440B_ABST
Abstract
Description
Technical Field
[0001] This invention belongs to the field of computer software development technology, and specifically relates to a Direct3D 12 descriptor compatibility method based on address same-origin mapping. Background Technology
[0002] VKD3D is an open-source project that emulates Direct3D 12 (hereinafter referred to as D3D12) based on the Vulkan 1.3 standard. Its core workflow is to translate D3D12 API calls on the Windows platform into Vulkan API calls on the Linux platform. One of its key dependencies is the VK_EXT_push_descriptor feature, which allows descriptor data to be pushed directly to the GPU execution stream while recording the Vulkan command buffer, without the need to pre-create descriptor sets and perform vkUpdateDescriptorSets update operations. This perfectly matches the core semantics of D3D12's root signature and immediate descriptor binding, that is, directly writing the GPU address of the descriptor table through SetGraphicsRootDescriptorTable. However, when the GPU does not support VK_EXT_push_descriptor, Vulkan defaults to a static model with descriptor pools, descriptor sets, pre-updates, and bindings. This is irreconcilable with the dynamic model of D3D12, causing errors when mapping D3D12 descriptors to Vulkan descriptors. This makes it difficult for the VKD3D translation layer to run efficiently and stably, ultimately making it difficult for Direct3D 12 applications to run compatibly on the Linux platform. Summary of the Invention
[0003] In view of this, the present invention provides a Direct3D 12 descriptor compatibility method based on address-origin mapping, which enables the compatible operation of D3D 12 applications even when the GPU does not support the VK_EXT_push_descriptor feature.
[0004] This invention provides a Direct3D 12 descriptor compatibility method based on address-origin mapping, which specifically includes the following steps:
[0005] During VKD3D initialization, the first association information is created, along with a layout fingerprint hash table using fingerprints and checksums as keys and pointers to descriptor set sub-pools as values. The first mapping table between the root signature and pipeline layout, the second mapping table between the descriptor heap and virtual descriptor heap partitions, and the third mapping table between the command buffer and the descriptor set are initialized. A virtual descriptor heap is created to generate CPU and GPU virtual base addresses with the same offset, achieving address source mapping between the CPU and GPU. The file descriptor of the virtual descriptor heap, the CPU virtual base address, the GPU virtual base address, and the handle stride are added to the first association information.
[0006] When a D3D application creates a descriptor heap, VKD3D extracts the descriptor heap type and descriptor handle stride, maps the descriptor heap type to a combination of Vulkan descriptor types, reads the first associated information, and if the handle stride is different from the descriptor handle stride, it reports an error and terminates the process; otherwise, it requests a virtual descriptor heap partition, creates a D3D12 descriptor heap wrapper object and adds it to the second mapping table, maps the CPU and GPU base addresses of the descriptor heap to the address offsets of the virtual descriptor heap partition, and implements the descriptor heap interface to map descriptor copying to direct CPU memory writing;
[0007] When creating the root signature, the static sampler and root parameters are converted into Vulkan samplers and pipeline layouts respectively, generating a layout fingerprint of the Vulkan pipeline layout. The descriptor set sub-pool corresponding to the layout fingerprint in the layout fingerprint hash table is reused. The root signature, Vulkan pipeline layout, and layout fingerprint are added to the first mapping table. When binding the root parameters, a descriptor push packet is generated according to the root parameter type and added to the descriptor push packet queue of the current command buffer. When performing drawing and scheduling, the drawing and scheduling commands are translated into Vulkan commands and submitted for execution. The Vulkan commands are submitted to the GPU, and the GPU executes the commands to complete the rendering.
[0008] Furthermore, the method for generating the layout fingerprint of the Vulkan pipeline layout is as follows: serialize all components of the Vulkan pipeline layout into intermediate data, perform SHA-256 hashing on the intermediate data to obtain the master fingerprint, perform CRC32 hashing on the intermediate data to obtain the checksum, and the master fingerprint and checksum constitute the layout fingerprint of the Vulkan pipeline layout.
[0009] Furthermore, the process of generating a descriptor push package based on the root parameter type when binding the root parameter involves parsing the root parameter index and the descriptor table GPU handle when the root parameter type is a descriptor table. The corresponding virtual descriptor heap partition in the second mapping table is then found based on the ID3D12 descriptor heap encapsulation object pointer. The offset of the descriptor table GPU handle in the virtual descriptor heap partition is calculated, the corresponding descriptor set index and the number of descriptors are obtained, and a descriptor push package is generated.
[0010] Furthermore, the process of generating a descriptor push packet based on the root parameter type during root parameter binding involves the following steps: When the root parameter type is a root constant, the root parameter index, constant start offset, constant quantity, and constant data pointer are parsed. The VkPushConstantRange information and split mapping corresponding to the root parameter are queried from the first mapping table. The CPU base address of the root constant in the virtual descriptor heap partition is calculated. Based on the CPU base address, the root constant data is written to the CPU-visible memory of the virtual descriptor heap partition. The data is automatically synchronized to the GPU, generating a descriptor push packet for the root constant.
[0011] Furthermore, the process of generating a descriptor push packet based on the root parameter type when binding the root parameter involves parsing the root parameter index and GPU virtual address when the root parameter type is a root descriptor, querying the descriptor set layout corresponding to the root parameter from the first mapping table, obtaining the Vulkan buffer object corresponding to the GPU virtual address, constructing Vulkan descriptor buffer information using the Vulkan buffer object, offset, and offset range, calculating the CPU address of the root descriptor in the virtual descriptor heap partition, serializing the Vulkan descriptor buffer information, writing it to the CPU address, and generating a descriptor push packet.
[0012] Furthermore, batch merging is performed on descriptor push packets in the descriptor push packet queue that have the same layout fingerprint and consecutive descriptor set indices, and the number of such packets exceeds the merging threshold.
[0013] Furthermore, the method for translating the drawing and scheduling commands into Vulkan commands and submitting them for execution is as follows:
[0014] If the descriptor push packet queue is empty, it is translated into a Vulkan command and submitted for execution; otherwise, the descriptor push packet queue is traversed to extract the layout fingerprint, the layout fingerprint hash table is queried to obtain the descriptor set sub-pool, the descriptor set, descriptor push packet, and command buffer handle are added to the third mapping table, the virtual descriptor heap is read in batches to update the descriptor set, and the D3D12 command is translated into a Vulkan command and submitted for execution.
[0015] The method for obtaining the descriptor set sub-pool by querying the layout fingerprint hash table is as follows: if there is an idle descriptor set in the descriptor set sub-pool, the head node of the linked list is taken out, marked as in use, and added to the in-use linked list; otherwise, if the remaining capacity of the descriptor set sub-pool is greater than the threshold, a new descriptor set is allocated, marked as in use, and added to the in-use linked list; if the remaining capacity is not greater than the threshold, a new descriptor pool is created, and the descriptor sets in use are migrated to the new pool, and then a new descriptor set is allocated.
[0016] Furthermore, the method of mapping descriptor copying to direct CPU memory writing is as follows: When applying descriptor copying, VKD3D intercepts and parses the source descriptor heap, target descriptor heap, source handle, target handle, and copy count; it obtains the CPU base address of the virtual descriptor heap partition corresponding to the source and target descriptor heaps according to the second mapping table, calculates the CPU address of the source and target descriptors in the virtual descriptor heap, and calculates it as follows: source CPU address = CPU base address of source descriptor heap + offset of source handle * handle step size, target CPU address = CPU base address of target descriptor heap + offset of target handle * handle step size; and directly performs CPU memory copying.
[0017] Furthermore, the method for converting the root parameter to a Vulkan pipeline layout is as follows: When the root parameter is a descriptor table type, the D3D12 descriptor table range is parsed, the D3D12 descriptor type is mapped to the corresponding Vulkan type, if it is a variable-size array, a Vulkan dynamic identifier is set and the dynamic offset index is recorded, if it is a fixed array, the quantity is assigned, a matching descriptor set is created with a single binding point configuration, and a Vulkan pipeline layout is generated; When the root parameter is a root constant, the root constant parameter is extracted to calculate the total number of bytes, the maximum push constant supported by the GPU is queried, if the total number of bytes is less than the maximum push constant, a VkPushConstantRange is created, otherwise multiple VkPushConstantRanges are created to split the root constant based on the quotient of the total number of bytes and the maximum push constant, and the split mapping relationship is recorded and the VkPushConstantRange array is saved; When the root parameter is a root descriptor, the D3D12 root descriptor type is mapped to a Vulkan type, a VkDescriptorSetLayoutBinding with fixed binding of 0 is created and a Vulkan pipeline layout is generated.
[0018] Furthermore, when the layout fingerprint hash table does not have a descriptor set subpool corresponding to the layout fingerprint, a new descriptor set subpool is created and a descriptor set is pre-allocated, specifically as follows:
[0019] The parameters of the descriptor set subpool are calculated. All descriptor set layouts in the Vulkan pipeline layout are traversed, and the total number of descriptors in each layout is counted. Expansion space is reserved as a multiple of this total to obtain the capacity of a single descriptor set. Based on the capacity of a single descriptor set and a pre-defined baseline value, the pre-allocated quantity is adjusted. The descriptor pool capacity is calculated according to a dynamic expansion rule that supports a set number of expansions. The maximum number of descriptors for different types is the product of the single descriptor set capacity and the descriptor pool capacity. A Vulkan descriptor pool is constructed based on the descriptor pool capacity, the number of descriptor types, the descriptor pool size array, and the flag indicating whether descriptor sets can be released. This pool is then stored in the descriptor set subpool. Allocation information is constructed to allocate a specified number of descriptor sets. All bindings are set to default values, and these sets are added to the free list of the descriptor set subpool and marked as free. The descriptor set subpool is initialized using the layout fingerprint, descriptor pool, descriptor set layout array, pre-allocated quantity, current total allocation, free list, in-use list, and recycling threshold. The layout fingerprint is used as the key to add the descriptor set subpool to the layout fingerprint hash table.
[0020] Beneficial effects:
[0021] This invention, when the GPU does not support the VK_EXT_push_descriptor feature, initializes core components such as a descriptor push packet cache queue, various mapping tables, a layout fingerprint hash table, and a static sampler cache pool. It also requests the Linux kernel to create a dual-space virtual descriptor heap with a source-mapped CPU and GPU address space. When creating a D3D12 descriptor heap in a D3D application, VKD3D maps the descriptor type, verifies the virtual descriptor heap handle step size, requests a virtual descriptor heap partition, and encapsulates the descriptor heap object. During root signature creation, it intercepts calls, converts root parameters to Vulkan pipeline layouts, generates layout fingerprints, and reuses or creates new descriptor set sub-pools. When binding root parameters, it parses different types of root parameters to generate descriptor push packets and merges them in batches. During drawing and scheduling, it obtains or allocates descriptor sets from the sub-pool, reads virtual descriptor heap metadata in batches to update the descriptor sets, and translates D3D12 commands into Vulkan commands. When submitting the command list, it associates Vulkan with the command. The fence updates the descriptor set reference count, submits Vulkan commands to the GPU to complete rendering, and achieves the adaptation of D3D12 semantics to Vulkan through the virtual descriptor heap. Attached Figure Description
[0022] Figure 1 This is a flowchart illustrating a Direct3D12 descriptor compatibility method based on address homo-source mapping 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 Direct3D12 descriptor compatibility method based on address-source mapping. Its core idea is as follows: When the GPU does not support the VK_EXT_push_descriptor feature, it initializes core components such as a descriptor push packet cache queue, various mapping tables, a layout fingerprint hash table, and a static sampler cache pool. It also requests the Linux kernel to create a dual-space virtual descriptor heap with address-source mapping between the CPU and GPU. When the D3D application creates the D3D12 descriptor heap, it maps the descriptor type using VKD3D, verifies the virtual descriptor heap handle step size, requests a virtual descriptor heap partition, and encapsulates the descriptor heap object. During root signature creation, it intercepts the call, converts the root parameters to a Vulkan pipeline layout, generates a layout fingerprint, and reuses or creates a new descriptor set sub-pool. When binding root parameters, it parses different types of root parameters to generate descriptor push packets and merges them in batches. During drawing and scheduling, it obtains or allocates descriptor sets from the sub-pool, reads virtual descriptor heap metadata in batches to update the descriptor sets, and translates D3D12 commands into Vulkan commands. When submitting the command list, it associates Vulkan with the descriptor. The fence updates the descriptor set reference count, submits Vulkan commands to the GPU to complete rendering, and achieves the adaptation of D3D12 semantics to Vulkan through the virtual descriptor heap.
[0025] This invention provides a Direct3D12 descriptor compatibility method based on address-origin mapping, the processing flow of which is as follows: Figure 1 As shown, the specific steps include:
[0026] Step 1: After VKD3D starts, create a Vulkan instance, obtain the GPU physical device handle, and detect whether the device supports the VK_EXT_push_descriptor feature. If it does, use VKD3D's native adaptation logic and end this process; otherwise, proceed to Step 2.
[0027] Step 2: During VKD3D initialization, a descriptor push packet cache queue, command flow association table, merging threshold, and first association information are created. The descriptor push packet memory pool is initialized. The first association information is the association information of the virtual descriptor heap in the kernel-mode dual space, used to store the file descriptor, CPU and GPU base addresses, and handle stride of the virtual descriptor heap returned by the kernel. A layout fingerprint hash table and a global descriptor pool template are created. The static sampler cache pool is initialized. The layout fingerprint hash table is a hash table with fingerprints and checksums as keys and pointers to descriptor set sub-pools as values. The static sampler cache pool is a hash table with static sampler descriptor serialization data as keys and Vulkan samplers as values. The first mapping relationship between descriptor root signatures and pipeline layout is initialized. The system generates a mapping table, a second mapping table describing the mapping relationship between the descriptor heap and the kernel-mode dual-space virtual descriptor heap partition, and a third mapping table describing the mapping relationship between the command buffer and the descriptor set; initializes the root parameter conversion auxiliary table, describing the mapping relationship between the D3D12 root parameter type and the Vulkan descriptor type; requests the Linux kernel to create a dual-space mapped virtual descriptor heap, generates user-mode CPU virtual base addresses and GPU virtual base addresses with the same offset, realizes address homogeneity mapping between CPU and GPU and completes initialization verification; describes the virtual descriptor heap by the number of bytes filled with the file descriptor of the virtual descriptor heap, user-mode CPU virtual base address, GPU virtual base address, handle step size and memory alignment; and saves the created virtual descriptor heap to the first associated information.
[0028] The first mapping table has a key of ID3D12 root signature interface pointer and a value of a first structure, which contains Vulkan pipeline layout, layout fingerprint, root parameter splitting information, and descriptor set layout array. The second mapping table has a key of ID3D12 descriptor heap encapsulation object pointer and a value of a second structure, which contains the file descriptor of the virtual descriptor heap, partition ID, start and end offsets within the virtual descriptor heap, CPU and GPU base addresses, descriptor type, and heap capacity. The third mapping table has a key of Vulkan command buffer and a value of a third structure, which contains descriptor set array, Vulkan Fence association information, and reference count.
[0029] The virtual descriptor heap includes a handle mapping table, a partition management table, a pre-update buffer pool, and a locking mechanism. The handle mapping table is a segmented hash table, divided into multiple partitions based on descriptor type. Each partition contains a set number of hash buckets, and each bucket is a doubly linked list. Each linked list node contains the offset within the virtual descriptor heap, descriptor metadata, reference count, an associated Vulkan object pointer, and a linked list pointer. The partition management table is a range tree structure, with nodes containing a start offset, end offset, partition ID, status, descriptor type, and reference count. The range tree supports partition lookup, allocation, and deallocation. The pre-update buffer pool contains multiple buffer blocks, each with parameters including size, GPU memory, CPU mapping address, GPU address, status, and reference count. The locking mechanism creates spinlocks for the handle mapping table, partition management table, and pre-update buffer pool to ensure multi-threaded concurrency safety.
[0030] Step 3: When the D3D application starts and creates the D3D12 descriptor heap, VKD3D parses the input parameters to extract the descriptor heap type, heap capacity, visibility flag, and descriptor handle step size. It maps the D3D12 descriptor heap type to a combination of Vulkan descriptor types. It reads the file descriptor and handle step size of the created virtual descriptor heap from the first association information. If the handle step size is different from the D3D12 handle step size, an error is reported and the process ends. Otherwise, it requests a virtual descriptor heap partition from the kernel, creates a D3D12 descriptor heap wrapper object, saves the D3D12 descriptor heap wrapper object to the second mapping table, maps the CPU and GPU base addresses of the D3D12 descriptor heap to the address offset of the virtual descriptor heap partition, implements the descriptor heap core interface, and maps descriptor copying to direct CPU memory writing.
[0031] The method of mapping descriptor copying to direct CPU memory writing is as follows: When the application copies descriptors, VKD3D intercepts and parses the source descriptor heap, target descriptor heap, source handle, target handle, and copy count; it obtains the CPU base address of the virtual descriptor heap partition corresponding to the source and target descriptor heaps according to the second mapping table, calculates the CPU address of the source and target descriptors in the virtual descriptor heap, and calculates it as follows: source CPU address = source descriptor heap CPU base address + source handle offset * handle step size, target CPU address = target descriptor heap CPU base address + target handle offset * handle step size; and then directly performs CPU memory copying.
[0032] The D3D12 descriptor heap encapsulation object contains the original D3D12 descriptor heap parameters, virtual descriptor heap partition association information, CPU base address of the descriptor heap, GPU base address of the descriptor heap, reference count, and interface virtual table.
[0033] Step 4: When the D3D application creates the D3D12 root signature, VKD3D intercepts the D3D12 root signature creation call, extracts the root parameter list and quantity, the static sampler list and quantity, and the root signature flag; it iterates through the static sampler list, converting the static samplers into Vulkan samplers; it initializes the first structure, iterates through the root parameter list, and converts it into the corresponding Vulkan pipeline layout according to the root parameter type; it serializes all components of the Vulkan pipeline layout into intermediate data, performs SHA-256 hashing on the intermediate data to obtain the master fingerprint, and performs CRC3 hashing on the intermediate data. 2. Obtain the checksum through hashing. The master fingerprint and checksum constitute the layout fingerprint of the Vulkan pipeline layout. Add the layout fingerprint to the first mapping table. Search for the layout fingerprint in the layout fingerprint hash table. If it exists, reuse the corresponding descriptor set sub-pool. Otherwise, create a new descriptor set sub-pool, pre-allocate the descriptor set, and add it to the layout fingerprint hash table. Add the root signature and Vulkan pipeline layout to the first mapping table. Add the mapping relationship between D3D12 root parameter type and Vulkan descriptor type to the root parameter conversion auxiliary table. Return the encapsulated ID3D12 root signature interface pointer to the D3D application.
[0034] The method for completing the conversion and caching of static samplers is as follows:
[0035] The sampler descriptor is serialized, and fields such as Filter, AddressU, AddressV, AddressW, and MipLODBias are packaged into a byte stream in a fixed order. The static sampler cache pool is queried. If the byte stream already exists, the corresponding Vulkan sampler is reused directly. If it does not exist, the attributes of the static sampler are mapped to the attributes of the Vulkan sampler. A Vulkan sampler is created based on the converted attributes and added to the static sampler cache pool.
[0036] The method for converting the root parameter type to the corresponding Vulkan pipeline layout component is as follows: For descriptor table types, the D3D12 descriptor table range is parsed, and the D3D12 descriptor type is mapped to the corresponding Vulkan type. If it is a variable-size array, a Vulkan dynamic identifier is set and the dynamic offset index is recorded. If it is a fixed array, the quantity is assigned, a matching descriptor set is created, a single binding point is configured, and a Vulkan pipeline layout is generated. For 32-bit root constants, the root constant parameters are extracted, the total number of bytes is calculated, and the maximum push constant supported by the GPU is queried. If the total number of bytes is less than the maximum push constant, a VkPushConstantRange is created. Otherwise, multiple VkPushConstantRanges are created to split the root constant based on the quotient of the total number of bytes and the maximum push constant, and the splitting mapping relationship is recorded. The VkPushConstantRange array is saved. For root descriptors, the D3D12 root descriptor type is mapped to a Vulkan type, a VkDescriptorSetLayoutBinding with fixed binding of 0 is created, and a Vulkan pipeline layout is generated.
[0037] The method for pre-allocating descriptor sets in creating new descriptor set sub-pools is as follows:
[0038] The parameters of the descriptor set sub-pool are calculated. All descriptor set layouts within the Vulkan pipeline are traversed, and the total number of descriptors in each layout is counted. Expansion space is reserved as a multiple of this total to obtain the capacity of a single descriptor set. Based on the capacity of a single descriptor set and a pre-set baseline value, the pre-allocated number is adjusted (e.g., 8 descriptors for larger capacities, 32 for smaller capacities), ensuring that the total pre-allocated capacity does not exceed the limit of the global descriptor pool template. The descriptor pool capacity is calculated according to the rule supporting dynamic expansion a set number of times. The maximum number of descriptors for different descriptor types is equal to the single descriptor set capacity multiplied by the descriptor pool capacity. The capacity product is calculated as follows: A Vulkan descriptor pool is constructed based on the descriptor pool capacity, the number of descriptor types, the descriptor pool size array, and the flag indicating whether descriptor sets can be released. This pool is then stored in the descriptor set sub-pool. Allocation information is constructed to allocate a specified number of descriptor sets, setting all bindings to default values to avoid dirty data. These sets are then added to the free list of the descriptor set sub-pool and marked as free. The descriptor set sub-pool is initialized using the layout fingerprint, descriptor pool, descriptor set layout array, pre-allocated quantity, current total allocation, free list, in-use list, and recycling threshold. The layout fingerprint is then added to the layout fingerprint hash table.
[0039] A descriptor range is the smallest unit of the D3D12 descriptor table and the core carrier connecting the semantics of the D3D12 descriptor table with the Vulkan descriptor set layout. It is a structured definition of a group of descriptors of the same type, with contiguous registers and binding points. It contains key information such as descriptor type, starting position, number, and register space, defining a continuous interval of a certain type of descriptor within the descriptor table. Descriptor ranges include SRV, UAV, CBV, or samplers. A descriptor table can contain multiple descriptor ranges, each corresponding to a contiguous set of descriptors of a certain type. It is the most basic unit for parsing D3D12 root parameters and converting them to the Vulkan descriptor set layout.
[0040] Step 5: When the D3D application binds the root parameter, VKD3D parses the root signature bound to the current command list to obtain the root signature pointer and queries the corresponding first structure from the first mapping table.
[0041] For descriptor table binding, the root parameter index and descriptor table GPU handle are parsed. The corresponding virtual descriptor heap partition in the second mapping table is found based on the ID3D12 descriptor heap encapsulation object pointer. The offset of the descriptor table GPU handle in the virtual descriptor heap partition is calculated. The corresponding descriptor set index and descriptor count are obtained. A descriptor push packet is generated. The descriptor push packet is added to the descriptor push packet queue of the current command buffer. Batch merging is performed on descriptor push packets with the same layout fingerprint and consecutive descriptor set indices that are greater than the merging threshold.
[0042] For root constant binding, the root parameter index, constant starting offset, constant quantity, and constant data pointer are parsed. The VkPushConstantRange information and split mapping corresponding to the root parameter are queried from the first mapping table. The CPU base address of the root constant in the virtual descriptor heap partition is calculated. The root constant data is written to the CPU visible memory of the virtual descriptor heap partition according to the CPU base address. The data is automatically synchronized to the GPU, and the descriptor push packet of the root constant is generated and added to the descriptor push packet queue of the current command buffer.
[0043] For root descriptor binding, the root parameter index and GPU virtual address are parsed. The descriptor set layout corresponding to the root parameter is queried from the first mapping table. The Vulkan buffer object corresponding to the GPU virtual address is obtained. The Vulkan descriptor buffer information is constructed using the Vulkan buffer object, offset, and offset range. The CPU address of the root descriptor in the virtual descriptor heap partition is calculated. The Vulkan descriptor buffer information is serialized and written to the CPU address to generate a descriptor push packet. The descriptor push packet is added to the descriptor push packet queue of the current command buffer.
[0044] Step 6: When the D3D application executes drawing and scheduling, VKD3D queries the descriptor push packet queue corresponding to the current command buffer according to the command flow association table. If the queue is empty, the drawing and scheduling are translated into the corresponding Vulkan commands and submitted for execution; otherwise, the descriptor push packet queue is traversed, the layout fingerprint of the descriptor push packet is extracted, and the layout fingerprint hash table is queried to obtain the corresponding descriptor set sub-pool. If there is an idle descriptor set in the descriptor set sub-pool, the head node of the linked list is taken out, marked as in use, and added to the in-use linked list; otherwise, if the remaining capacity of the descriptor set sub-pool is greater than the threshold, a new descriptor set is allocated, marked as in use, and added to the in-use linked list. If the remaining capacity is not greater than the threshold, a new descriptor pool is created, and the descriptor sets in use are migrated to the new pool, and a new descriptor set is allocated again; the descriptor set, the corresponding descriptor push packet, and the command buffer handle are added to the third mapping table; the descriptor metadata in the virtual descriptor heap is read in batches, the descriptor set is updated according to the metadata, and the D3D12 commands are translated into Vulkan commands.
[0045] Furthermore, when VKD3D updates the Vulkan descriptor set, the kernel layer reads descriptor metadata from the virtual descriptor heap in batches and pre-updates the buffer padding, specifically as follows:
[0046] The kernel determines the core data structure of the virtual descriptor heap through file descriptors, acquires the spinlock of the handle mapping table, calculates the descriptor traversal step size and offset range, acquires the spinlock of the pre-update buffer pool, selects a free buffer block according to the polling algorithm, marks its status as occupied and increments the reference count by 1; initializes the buffer block write pointer, traverses the offset range, retrieves the descriptor metadata from the linked list node of the handle mapping table, constructs the fourth structure, writes the fourth structure to the set position of the buffer block, and returns the number of pre-update buffer blocks used and the buffer block information array to VKD3D.
[0047] Step 7: When the D3D application submits the command list, VKD3D parses the command list array and the number of commands, traverses each command list to extract the Vulkan command buffer and the associated fence, constructs the Vulkan submission information and creates the associated Vulkan fence, obtains all descriptor sets associated with the current command buffer through the third mapping table, updates the reference count and synchronization information for each descriptor set, submits the Vulkan command to the GPU, and the GPU executes the command to complete the rendering.
[0048] Example:
[0049] This embodiment employs a Direct3D 12 descriptor compatibility method based on address-source mapping provided by the present invention. It enables the normal operation of D3D 12 applications in a GPU hardware environment that does not support VK_EXT_push_descriptor. The specific process includes:
[0050] S1. System initialization and VK_EXT_push_descriptor support detection.
[0051] S1.1 When VKD3D starts, it executes the initialization process:
[0052] Call vkCreateInstance to create a Vulkan instance, passing in a list of supported extensions, including basic extensions such as VK_KHR_get_physical_device_properties2;
[0053] vkEnumeratePhysicalDevices obtains handles to all GPU physical devices in the system, and iterates through each device to perform feature detection.
[0054] Construct a VkPhysicalDeviceFeatures2 structure, where pNext points to a VkPhysicalDevicePushDescriptorFeaturesEXT structure, and call vkGetPhysicalDeviceFeatures2 to query the feature set.
[0055] Check the VkPhysicalDevicePushDescriptorFeaturesEXT::pushDescriptor field: if it is VK_TRUE, use the native VK_EXT_push_descriptor adaptation logic of VKD3D; if it is VK_FALSE, determine that the GPU does not support the target feature and automatically trigger the compatibility mode of this invention.
[0056] S1.2 After compatibility mode is triggered, VKD3D initializes the user-mode core module.
[0057] S1.2.1 Initialize the Descriptor Push Engine (DPE).
[0058] Create a DPE control block (DpeControlBlock) containing: a descriptor push packet (DPP) cache queue, a command flow association table, a merge threshold configuration, and VHD association information. The DPP cache queue is a circular queue structure with an initial capacity of 64 elements, each a pointer to a DescriptorPushPackage. The command flow association table is a hash table with the key being a Vulkan command buffer handle (VkCommandBuffer) and the value being a pointer to a DppQueue structure. The merge threshold is set when, for example, three consecutive calls to SetGraphicsRootDescriptorTable with the same layout fingerprint trigger a batch merge. The VHD association information stores the VHD FD, CPU / GPU base address, and handle step size returned by the kernel.
[0059] Initialize the DPP memory pool: pre-allocate 1024 DescriptorPushPackage objects, including flexible array space, and manage them using a free linked list to avoid frequent memory allocation and release;
[0060] S1.2.2 Initialize the layout fingerprint-driven lazy-loaded descriptor set pool LF-LDP.
[0061] Create the LF-LDP global manager LfLdpGlobalManager, which includes: a layout fingerprint hash table, a global descriptor pool template, and a reclamation policy configuration. The layout fingerprint hash table, LayoutFingerprintHashTable, has a 256-bit fingerprint and a 32-bit checksum as keys, and a DescriptorSetSubPool pointer as the value. The global descriptor pool template, GlobalDescriptorPoolTemplate, predefines supported descriptor type combinations as SRV, CBV, UAV, sampler, storage buffer, and unified buffer, with an initial maximum capacity of 4096 for each type and a memory attribute of VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT. The reclamation policy configuration includes an idle percentage threshold of 70%, a maximum idle time of 500ms, an LRU replacement list, and a pre-allocated base of 16 descriptors.
[0062] Initialize the static sampler cache pool StaticSamplerCachePool, which is a hash table structure. The keys are the serialized data of the static sampler descriptors, and the values are VkSampler objects to avoid duplicate sampler creation.
[0063] S1.2.3 Initialize the D3D12-Vulkan mapping table set.
[0064] Root Signature and Pipeline Layout Mapping Table RootSigPipelineLayoutMap: The key is an ID3D12RootSignature interface pointer, and the value is a RootSigPipelineLayoutMapping structure, which contains VkPipelineLayout, layout fingerprint, root parameter splitting information, and descriptor set layout array;
[0065] Descriptor heap and VHD partition mapping table DescHeapVhdRegionMap: The key is an ID3D12DescriptorHeap interface pointer, and the value is a DescHeapVhdMapping structure, which contains VHD FD, partition ID, start offset within VHD, start and end offset within VHD, CPU and GPU base address, descriptor type, and heap capacity.
[0066] The command buffer and descriptor set association table CmdBufDescSetMap has a key of VkCommandBuffer and a value of a CmdBufDescSetBinding structure, which contains an array of descriptor sets, fence association information, and reference counts.
[0067] S1.2.4 Initialize the root parameter conversion auxiliary table: Predefine the mapping table from D3D12 root parameter type to Vulkan descriptor type or Push Constant, such as D3D12_ROOT_PARAMETER_TYPE_CBV to VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS to VK_DESCRIPTOR_TYPE_PUSH_CONSTANT, etc., covering all D3D12 root parameter types.
[0068] S1.3, VKD3D requests the kernel to create a virtual descriptor heap (VHD) with dual-space mapping.
[0069] Construct the SysVkd3dVhdInitParams parameter for the sys_vkd3d_vhd_init system call:
[0070] typedef struct {
[0071] uint32_t descriptor_type_count; / / The number of supported descriptor types, fixed at 6 here (covering all core types)
[0072] VkDescriptorType descriptor_types[6]; / / List of descriptor types: VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_DESCRIPTOR_TYPE_SAMPLER, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER
[0073] uint64_t total_capacity; / / Total capacity: 1048576 (1M descriptors)
[0074] VkMemoryPropertyFlags memory_properties; / / Memory properties: VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
[0075] uint32_t handle_stride; / / Handle stride: 8 bytes (consistent with D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV)
[0076] int32_t FD; / / Output: VHD file descriptor
[0077] uint64_t gpu_base_addr; / / Output: GPU virtual base address (VkDeviceAddress) of the VHD
[0078] uint64_t cpu_base_addr; / / Output: User-space CPU virtual base address of the VHD
[0079] uint32_t alignment_padding; / / Output: Number of bytes for memory alignment padding (ensuring consistent CPU / GPU address offsets)
[0080] SysVkd3dVhdInitParams;
[0081] Calling syscall(SYS_vkd3d_vhd_init, ¶ms) triggers the kernel VHD creation process, then blocks and waits for the kernel to return.
[0082] S1.4 The kernel layer processes the sys_vkd3d_vhd_init call to create a dual-space VHD.
[0083] S1.4.1 After receiving the parameters, the kernel VHD module performs initialization verification.
[0084] Verify the validity of memory_properties, which must include DEVICE_LOCAL_BIT and HOST_VISIBLE_BIT; verify that handle_stride is an integer multiple of the GPU page size (4KB or 64KB), otherwise automatically align upwards.
[0085] S1.4.2, Request a GPU-visible memory block.
[0086] Call vkGetPhysicalDeviceMemoryProperties to get the list of GPU memory types, and filter out the memory type index mem_type_idx that meets the memory_properties;
[0087] Calculate the total size of the memory block: total_size = total_capacity * handle_stride + alignment_padding, where alignment_padding is the padding bytes required for GPU memory alignment, which is deduced from the alignmentOffset returned by vkGetPhysicalDeviceMemoryProperties;
[0088] Construct VkMemoryAllocateInfo, allocationSize = total_size, memoryTypeIndex = mem_type_idx, call vkAllocateMemory to request GPU memory, and obtain VkDeviceMemory vhd_mem.
[0089] S1.4.3, Implement CPU-GPU dual-space homogeneous mapping.
[0090] Calling `vkMapMemory(device, vhd_mem, 0, total_size, 0, &kernel_cpu_addr)` maps GPU memory to the kernel-mode CPU address space;
[0091] Call `vkGetDeviceMemoryAddress(device, &(VkDeviceMemoryAddressInfo){.sType = VK_STRUCTURE_TYPE_DEVICE_MEMORY_ADDRESS_INFO, .memory = vhd_mem}, &gpu_base_addr)` to obtain the GPU virtual address;
[0092] An anonymous inode node is created in kernel space, and information such as vhd_mem, kernel_cpu_addr, and gpu_base_addr are bound to the inode;
[0093] Call dma_buf_export to encapsulate the inode into a DMA-BUF file descriptor, and set ops to a custom VHD operation set, which includes callbacks such as mmap and release;
[0094] When user space calls mmap, the kernel maps kernel_cpu_addr from the kernel space to the user space address space using remap_pfn_range: it calculates pfn = virt_to_pfn(kernel_cpu_addr) and calls remap_pfn_range(vma, vma->vm_start, pfn, vma->vm_end - vma->vm_start, vma->vm_page_prot) to ensure that the offsets of the user space CPU address are completely consistent with those of the kernel space and GPU address, i.e., same-source mapping.
[0095] S1.4.4 Initialize the VHD core data structure.
[0096] The handle mapping table VhdHandleMap is a segmented hash table divided into 6 partitions based on descriptor type. Each partition contains 32 hash buckets, and each bucket is a doubly linked list. The linked list node is a VhdHandleMapNode, which contains the offset_in_vhd (8 bytes) within the VHD, the descriptor metadata VkDescriptorDataUnion (containing VkDescriptorImageInfo or VkDescriptorBufferInfo), the reference count ref_count (atomic 32 bits), the associated Vulkan object pointer void*vulkan_obj (such as VkImageView), and the linked list pointer.
[0097] The partition management table VhdRegionManager has a range tree structure, with nodes named VhdRegionNode, containing: start offset start_offset, end offset end_offset, partition ID region_id (globally unique), state (idle or allocated), descriptor type desc_type, and reference count ref_count; the range tree supports O (logN) partition queries, allocation, and release.
[0098] The pre-update buffer pool VhdPreupdateBufferPool contains 16 buffer blocks VhdPreupdateBuffer. Each buffer block has the following parameters: size 65536 bytes (64KB), GPU memory VkDeviceMemory, CPU mapping address cpu_addr, GPU address VkDeviceAddress, state (idle or occupied), and reference count ref_count. The buffer block creation process is as follows: vkAllocateMemory is called to allocate memory (attributes are the same as VHD), and vkMapMemory is called to map it to the kernel-mode CPU address.
[0099] Locking mechanism: Spin locks are created for the handle mapping table (one spin lock per partition), the partition management table (one spin lock), and the pre-update buffer pool (one spin lock) to ensure multi-threaded concurrency safety;
[0100] The kernel returns the following result: It fills the VHD's DMA-BUF FD, gpu_base_addr, user-space cpu_base_addr (vm_start), and alignment_padding into the SysVkd3dVhdInitParams output field and wakes up the blocked VKD3D process.
[0101] S1.4.5 After receiving the result returned by the kernel, VKD3D completes the initialization and cleanup.
[0102] Store the VHD's FD, CPU and GPU base addresses, handle step size, and alignment padding bytes into the DPE control block; verify the homogeneity of CPU and GPU addresses: calculate (cpu_base_addr + offset) - cpu_base_addr == (gpu_base_addr + offset) - gpu_base_addr (test with multiple offset values) to ensure that the offsets are consistent; initialize the DPP cache queue and add the pre-allocated DescriptorPushPackage object to the free list.
[0103] S2 and D3D12 descriptor heap creation interface conversion, mapping from D3D12 resource creation to VHD partitions.
[0104] S2.1 When a D3D12 application calls ID3D12Device::CreateDescriptorHeap, VKD3D intercepts the call:
[0105] Parse the input parameter D3D12_DESCRIPTOR_HEAP_DESC and extract key information:
[0106] Descriptor heap type (Type: such as D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
[0107] Heap capacity (NumDescriptors: number of descriptors);
[0108] Visibility flags (e.g., D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE);
[0109] Descriptor handle stride (NodeMask: multi-GPU scenario, default is 1 here);
[0110] Verify parameter validity: If NumDescriptors is 0 or exceeds the remaining capacity of the VHD, return D3D12_ERROR_INVALID_ARGUMENT.
[0111] S2.2 Mapping D3D12 descriptor types to Vulkan types.
[0112] The corresponding Vulkan descriptor type combination is determined based on D3D12_DESCRIPTOR_HEAP_TYPE, such as D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV corresponding to VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, etc.
[0113] Obtain the FD and handle step size of the VHD from the DPE control block, ensuring that it is consistent with the D3D12 step size (e.g., 8 bytes).
[0114] S2.3, Request a VHD partition from the kernel:
[0115] Construct the SysVkd3dVhdReserveRegionParams system call parameter:
[0116] typedef struct {
[0117] int32_t FD; / / VHD file descriptor
[0118] VkDescriptorType primary_desc_type; / / Primary descriptor type (corresponding heap type)
[0119] uint64_t num_descriptors; / / Number of descriptors requested
[0120] uint64_t start_offset; / / Output: The starting offset of the partition within the VHD (in bytes)
[0121] uint64_t end_offset; / / Output: End offset of the partition within the VHD (bytes)
[0122] uint32_t region_id; / / Output: Partition ID (globally unique)
[0123] uint32_t pad; / / Pad bytes to ensure 64-bit alignment
[0124] } SysVkd3dVhdReserveRegionParams;
[0125] Enter the parameters: FD is the FD of VHD, primary_desc_type is the primary type after mapping, num_descriptors is NumDescriptors, and call syscall(SYS_vkd3d_vhd_reserve_region, ¶ms).
[0126] S2.4 Kernel layer handles partition requests:
[0127] The kernel uses file descriptors (FDs) to locate the corresponding VHD core data structure and acquires a spin lock for the partition management table.
[0128] Calculate the requested memory size: request_size = num_descriptors * handle_stride;
[0129] Interval tree query for free partitions: The first-fit algorithm is used to traverse the interval tree to find the first free partition with a capacity greater than or equal to request_size.
[0130] Partition adaptation handling:
[0131] If a matching partition is found: split it into an allocated partition (size is request_size) and a remaining free partition (original partition size - request_size), update the interval tree node, mark the state of the allocated partition as allocated, and assign a region_id generated by an auto-incrementing counter;
[0132] If no matching partition is found: trigger the VHD expansion process, that is, call vkAllocateMemory to request a new GPU memory block with the same capacity as the original VHD, copy the original VHD data to the new memory block through vkCmdCopyMemory, update the VHD's gpu_base_addr and kernel_cpu_addr, remap the user-space CPU address, and then execute the partition request again.
[0133] Calculate start_offset and end_offset: end_offset = start_offset + request_size, fill in the output parameters; release the spin lock of the partition management table and return the parameters to VKD3D.
[0134] S2.5, VKD3D creates a D3D12 descriptor heap encapsulated object.
[0135] The D3D12DescriptorHeapWrapper, a wrapper structure for allocating the ID3D12DescriptorHeap interface, contains:
[0136] The original D3D12 descriptor heap parameters include Type, NumDescriptors, and Flags;
[0137] VHD partition association information, including FD, region_id, start_offset, and end_offset;
[0138] The CPU base address of the descriptor heap is cpu_heap_start = VHD.cpu_base_addr + start_offset;
[0139] The GPU base address of the descriptor heap is gpu_heap_start = VHD.gpu_base_addr + start_offset;
[0140] The reference count, ref_count, is initialized to 1.
[0141] The interface virtual table implements all methods of ID3D12DescriptorHeap, such as GetGPUDescriptorHandleForHeapStart and GetCPUDescriptorHandleForHeapStart;
[0142] Store the D3D12DescriptorHeapWrapper pointer into the descriptor heap and VHD partition mapping table, with the key being the ID3D12DescriptorHeap interface pointer of the encapsulated object.
[0143] S2.6 Implement the core interface of the D3D12 descriptor heap.
[0144] GetGPUDescriptorHandleForHeapStart: Directly returns gpu_heap_start, with the ptr field of D3D12_GPU_DESCRIPTOR_HANDLE set to this address;
[0145] GetCPUDescriptorHandleForHeapStart: Directly returns cpu_heap_start, with the ptr field of D3D12_CPU_DESCRIPTOR_HANDLE set to this address;
[0146] Release: Decrement the reference count by 1. If the reference count is 0, the subsequent partition release process is triggered.
[0147] S2.7, Handling the D3D12 descriptor copy interface ID3D12Device::CopyDescriptors.
[0148] When an application calls CopyDescriptors to copy descriptors, VKD3D intercepts the call, parses the source and target descriptor heaps, source and target handles, and the number of copies; it then obtains the VHD partition CPU base address corresponding to the source and target heaps through the descriptor heap-VHD partition mapping table.
[0149] Calculate the CPU addresses of the source and target descriptors in the VHD: src_cpu_addr = source heap.cpu_heap_start + source handle.offset * handle step size, dst_cpu_addr = target heap.cpu_heap_start + target handle.offset * handle step size;
[0150] Directly perform CPU memory copy: memcpy(dst_cpu_addr, src_cpu_addr, number of copies * handle step);
[0151] Because VHD memory has the HOST_COHERENT_BIT attribute, there is no need to call vkFlushMappedMemoryRanges. CPU writes are automatically synchronized to GPU-visible memory, fully simulating D3D12 descriptor copy semantics and avoiding calls to vkUpdateDescriptorSets.
[0152] The S3 and D3D12 root signature creation interface is converted, mapping from D3D12 resource creation to Pipeline Layout.
[0153] S3.1 When a D3D12 application calls ID3D12Device::CreateRootSignature, VKD3D intercepts the call:
[0154] Parse the input parameters D3D12_ROOT_SIGNATURE_DESC and D3D12_ROOT_SIGNATURE_FLAGS to extract key information: the root parameter list pParameters and its quantity NumParameters; the static sampler list pStaticSamplers and its quantity NumStaticSamplers; and the root signature flags, such as D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT. Verify the validity of the root parameters: if NumParameters exceeds the maximum number of sets supported by Vulkan Pipeline Layout (default 4), return D3D12_ERROR_INVALID_ARGUMENT.
[0155] S3.2 Static Sampler Conversion and Buffering:
[0156] Iterate through each D3D12_STATIC_SAMPLER_DESC and perform the following operations:
[0157] Serialization sampler descriptor: Packs fields such as Filter, AddressU, AddressV, AddressW, and MipLODBias into a byte stream sampler_serial_data in a fixed order;
[0158] Query the static sampler cache pool: if sampler_serial_data already exists, reuse the corresponding VkSampler directly; if it does not exist, perform the transformation.
[0159] Convert D3D12 sampler attributes to Vulkan using mapping tables, such as D3D12_FILTER_MIN_MAG_MIP_LINEAR → VK_FILTER_LINEAR, D3D12_TEXTURE_ADDRESS_MODE_CLAMP → VK_SAMPLER_ADDRESS_MOD
[0160] Construct VkSamplerCreateInfo, call vkCreateSampler to create a sampler, and store it in the cache pool.
[0161] S3.3, Root Parameter Transformation and Dynamic Layout Adaptation Layer Construction:
[0162] S3.3.1 Initialize the RootSigPipelineLayoutMapping structure to store the transformation results.
[0163] S3.3.2. Traverse each D3D12_ROOT_PARAMETER root parameter and convert it according to its type.
[0164] Type 1: D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE descriptor table:
[0165] Parse D3D12_DESCRIPTOR_TABLE_LAYOUT to extract the descriptor range list pDescriptorRanges and the number NumDescriptorRanges;
[0166] Iterate through each D3D12_DESCRIPTOR_RANGE and extract: RangeType, BaseShaderRegister, NumDescriptors, RegisterSpace, and OffsetInDescriptorsFromTableStart;
[0167] Mapping range types to Vulkan descriptor types: e.g., D3D12_DESCRIPTOR_RANGE_TYPE_SRV→VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, D3D12_DESCRIPTOR_RANGE_TYPE_UAV→VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
[0168] Handling variable-size arrays: If NumDescriptors == UINT_MAXD3D12 is the variable array identifier, then set VkDescriptorSetLayoutBinding::descriptorCount = VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT, record the dynamic offset index dynamic_offset_idx of this binding, and subsequently set the actual number through vkCmdSetDescriptorSetDynamicOffset;
[0169] Handling fixed-size arrays: If NumDescriptors is a fixed value, simply set descriptorCount = NumDescriptors;
[0170] Create a VkDescriptorSetLayoutBinding struct:
[0171] binding = BaseShaderRegister is consistent with the D3D12 register to ensure shader access compatibility;
[0172] descriptorType = mapped Vulkan type;
[0173] descriptorCount = the calculated value above;
[0174] stageFlags = Derived shader stage, such as the ShaderVisibility mapping of the D3D12 root parameters;
[0175] pImmutableSamplers = pointers to static samplers if they are of type SRV and associated with static samplers;
[0176] Store all VkDescriptorSetLayoutBindings into VkDescriptorSetLayoutCreateInfo::pBindings;
[0177] The vkCreateDescriptorSetLayout function creates a descriptor set layout, VkDescriptorSetLayout, and stores it in the RootSigPipelineLayoutMapping::descriptor_set_layouts array. The set index is the same as the index of the root parameter in the root signature.
[0178] Type 2: D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS root constants:
[0179] Extract the root constant parameters: quantity Num32BitValues, start shader register ShaderRegister, register space RegisterSpace, shader visibility ShaderVisibility;
[0180] Calculate the total number of bytes for root constants: total_size = Num32BitValues * 4 (4 bytes for each root constant);
[0181] To query the maximum Push Constant size supported by the GPU, call vkGetPhysicalDeviceProperties to get maxPushConstantsSize;
[0182] Root constant splitting:
[0183] If total_size ≤ maxPushConstantsSize: Create 1 VkPushConstantRange, offset = ShaderRegister * 4, size = total_size, stageFlags = mapped shader stage;
[0184] If total_size > maxPushConstantsSize: Split into multiple VkPushConstantRange based on maxPushConstantsSize. For example, if the total size is 256 bytes and the maximum supported size is 128 bytes, split into 2, each with size = 128, and offsets of ShaderRegister*4 and ShaderRegister*4 + 128 respectively. Record the range_count and range_index_map after splitting for subsequent push order mapping.
[0185] Store the VkPushConstantRange array into RootSigPipelineLayoutMapping::push_constant_ranges.
[0186] Type 3: D3D12_ROOT_PARAMETER_TYPE_CBV / SRV / UAV root descriptor:
[0187] Map root descriptor types to Vulkan descriptor types, such as D3D12_ROOT_PARAMETER_TYPE_CBV → VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
[0188] Create VkDescriptorSetLayoutBinding: binding = 0 root descriptor fixed binding 0, descriptorCount = 1, descriptorType = mapped type, stageFlags = shader visibility;
[0189] Create a descriptor set layout VkDescriptorSetLayout, with the set index matching the root parameter index;
[0190] Mark this binding as a root descriptor binding and store it in RootSigPipelineLayoutMapping::root_descriptor_bindings.
[0191] S3.4. Create the Vulkan Pipeline Layout.
[0192] Construct the VkPipelineLayoutCreateInfo structure:
[0193] setLayoutCount = the number of descriptor tables / root descriptors in the root parameter;
[0194] pSetLayouts = RootSigPipelineLayoutMapping::descriptor_set_layouts;
[0195] pushConstantRangeCount = Number of VkPushConstantRanges after splitting;
[0196] pPushConstantRanges = RootSigPipelineLayoutMapping::push_constant_ranges;
[0197] vkCreatePipelineLayout creates VkPipelineLayout and stores it in RootSigPipelineLayoutMapping::pipeline_layout.
[0198] S3.5, Layout fingerprint generation.
[0199] The layout fingerprint is implemented using structured serialization and double hash verification to ensure uniqueness and collision resistance.
[0200] Serialize all components of the Pipeline Layout:
[0201] Set the number of layouts written to the descriptor set using `setLayoutCount`.
[0202] Iterate through each descriptor set layout: write the binding, descriptorType, descriptorCount, stageFlags, and number of pImmutableSamplers to the VkDescriptorSetLayoutBinding;
[0203] Write the number of Push Constant Ranges (pushConstantRangeCount);
[0204] Iterate through each Push Constant Range: write stageFlags, offset, and size;
[0205] Write the number of static samplers and the serialized data for each sampler;
[0206] Generate master hash SHA-256: Perform SHA-256 hash on the serialized data to obtain a 256-bit master fingerprint; Generate checksum CRC32: Perform CRC32 calculation on the serialized data to obtain a 32-bit checksum;
[0207] Combined layout fingerprint: The final fingerprint is a 256-bit master fingerprint and a 32-bit checksum, ensuring that different layouts will not generate the same fingerprint. The layout fingerprint is stored in RootSigPipelineLayoutMapping::layout_fingerprint.
[0208] S3.6. Initialize the LF-LDP sub-pool.
[0209] VKD3D passes the layout fingerprint to the LF-LDP global manager and queries the layout fingerprint hash table:
[0210] If the fingerprint already exists, the corresponding DescriptorSetSubPool sub-pool is reused directly; if the fingerprint does not exist, a new sub-pool DescriptorSetSubPool is created, and the pre-allocation descriptor set process is executed.
[0211] Descriptor set pre-allocation:
[0212] Calculate sub-pool parameters: Single descriptor set capacity: Traverse all descriptor set layouts of the Pipeline Layout, sum the descriptorCount of each layout, multiply by 2 (reserve expansion space), and obtain single_set_capacity; Pre-allocated quantity: Based on the baseline value (16) of the LF-LDP global configuration, adjust in combination with single_set_capacity, such as pre-allocating 8 when the capacity is large, and pre-allocating 32 when the capacity is small, ensuring that the total pre-allocated capacity does not exceed the limit of the global descriptor pool template; Descriptor pool capacity: max_sets = pre-allocated quantity * 4 (supports dynamic expansion 3 times), max_descriptors is allocated according to descriptor type, and each type is single_set_capacity * max_sets.
[0213] Create a dedicated descriptor pool for the sub-pool:
[0214] Construct the VkDescriptorPoolSize array: allocate max_descriptors according to the descriptor type;
[0215] Construct VkDescriptorPoolCreateInfo: maxSets = max_sets, poolSizeCount = number of descriptor types, pPoolSizes = array of VkDescriptorPoolSize, flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, allowing the release of descriptor sets; call vkCreateDescriptorPool to create a descriptor pool (VkDescriptorPool) and store it in a sub-pool.
[0216] Pre-allocated descriptor set:
[0217] Construct the VkDescriptorSetAllocateInfo array: descriptorPool = sub-pool descriptor pool, descriptorSetCount = pre-allocated number, pSetLayouts = array of descriptor set layouts for this Pipeline Layout; call vkAllocateDescriptorSets to allocate the pre-allocated number of descriptor sets VkDescriptorSet; initialize each pre-allocated descriptor set: call vkUpdateDescriptorSets to set all bindings to default values, such as VkDescriptorImageInfo::imageView = VK_NULL_HANDLE, to avoid dirty data; add the pre-allocated descriptor sets to the LRU list of the sub-pool and mark their status as free.
[0218] Initialize sub-pool management data:
[0219] The sub-pool contains: layout fingerprint, descriptor pool, descriptor set layout array, pre-allocation quantity, current total allocation quantity, free list (LRU), in-use list, and reclamation threshold (70% free percentage); the sub-pool is stored in the layout fingerprint hash table of LF-LDP, with the layout fingerprint as the key.
[0220] S3.7 Complete the root signature mapping.
[0221] Store the ID3D12RootSignature interface pointer and the RootSigPipelineLayoutMapping structure into the root signature and Pipeline Layout mapping table; return the encapsulated ID3D12RootSignature interface pointer to the D3D12 application, and all subsequent binding operations of this root signature will be associated with the corresponding Pipeline Layout through this pointer.
[0222] S4 and D3D12 root parameter binding interface conversion, responsible for the complete conversion of D3D12 root parameter binding interface, including descriptor table, root constant, root CBV, SRV, and UAV.
[0223] S4.1 Binding Interface Interception and Classification.
[0224] VKD3D intercepts all D3D12 root parameter binding interfaces, including:
[0225] Binding to the descriptor table using SetGraphicsRootDescriptorTable or SetComputeRootDescriptorTable; binding to the root constants using SetGraphicsRoot32BitConstants or SetComputeRoot32BitConstants; binding to the root CBV using SetGraphicsRootConstantBufferView or SetComputeRootConstantBufferView; binding to the root SRV using SetGraphicsRootShaderResourceView or SetComputeRootShaderResourceView; binding to the root UAV using SetGraphicsRootUnorderedAccessView or SetComputeRootUnorderedAccessView.
[0226] Parse the root signature bound to the current command list: Obtain the bound root signature pointer through ID3D12GraphicsCommandList::GetPipelineState, and query the corresponding RootSigPipelineLayoutMapping structure containing Pipeline Layout, layout fingerprint, and root parameter information from the root signature and Pipeline Layout mapping table.
[0227] S4.2 Descriptor table binding conversion, corresponding to SetGraphicsRootDescriptorTable.
[0228] Parse the input parameters: RootParameterIndex, DescriptorTable (GPU handle), and D3D12_GPU_DESCRIPTOR_HANDLE.
[0229] Verify the validity of the descriptor handle:
[0230] By using the descriptor heap and VHD partition mapping table, the corresponding VHD partition DescHeapVhdMapping is found based on the base address DescriptorTable.ptr and VHD.gpu_base_addr;
[0231] Calculate the handle offset within the VHD: vhd_offset = DescriptorTable.ptr - VHD.gpu_base_addr;
[0232] Verify offset validity: vhd_offset ≥ partition.start_offset and vhd_offset + number of descriptors * handle step size ≤ partition.end_offset. If invalid, return D3D12_ERROR_INVALID_DESCRIPTOR_HANDLE.
[0233] Get the corresponding Set index and number of descriptors:
[0234] Set index = RootParameterIndex;
[0235] The number of descriptors is obtained from the application's indirect parameters when querying the descriptor table corresponding to the root parameter in RootSigPipelineLayoutMapping, which has a fixed capacity or is a dynamically variable array.
[0236] Generate Descriptor Push Package (DPP):
[0237] Obtain a free DescriptorPushPackage object from the DPP memory pool of the DPE;
[0238] Populate the DPP field:
[0239] magic = 0x44505056VPPD, used for command stream verification;
[0240] set_index = Set index;
[0241] descriptor_count = the number of descriptors;
[0242] layout_fingerprint[0..7] = The 256-bit master hash of the layout fingerprint is divided into 8 32-bit fields;
[0243] layout_checksum = 32-bit checksum of the layout fingerprint;
[0244] vhd_start_offset = vhd_offset;
[0245] vhd_end_offset = vhd_offset + number of descriptors * handle stride;
[0246] descriptor_type = The primary descriptor type of this descriptor table;
[0247] `dynamic_offset_count` is 1 if it's a variable array, otherwise it's 0.
[0248] dynamic_offsets[0] = the actual number of the variable array, if any;
[0249] Adding a DPP to the DPP queue of the current command buffer is done by querying the command flow association table.
[0250] DPP merge check:
[0251] If the last DPP in the current DPP queue has the same layout fingerprint as the newly generated DPP, and the Set indices are consecutive, then a merge is triggered:
[0252] Update the descriptor_count of the last DPP by incrementing the number of new DPPs;
[0253] Update vhd_end_offset to the new DPP's vhd_end_offset;
[0254] If dynamic offsets exist, append them to the dynamic_offsets array;
[0255] Return the new DPP to the DPP memory pool;
[0256] If the merging conditions are not met, the new DPP is added to the tail of the queue.
[0257] S4.3, Root constant binding conversion, corresponding to SetGraphicsRoot32BitConstants.
[0258] Parse the input parameters: Root parameter index RootParameterIndex, constant start offset DestOffsetIn32BitValues, constant quantity Num32BitValues, and constant data pointer pSrcData;
[0259] Retrieve the Push Constant Range information (push_constant_ranges) and split mapping (range_index_map) corresponding to the root parameter from RootSigPipelineLayoutMapping.
[0260] Calculate the target address: the CPU base address of the root constant in the VHD: vhd_cpu_addr = VHD.cpu_base_addr + VHD partition corresponding to the root parameter.offset + DestOffsetIn32BitValues * 4.
[0261] Write to VHD memory: Call memcpy(vhd_cpu_addr, pSrcData, Num32BitValues * 4) to write the root constant data to the CPU-visible memory of the VHD; since the VHD has the HOST_COHERENT_BIT attribute, the data is automatically synchronized to the GPU.
[0262] Generate root constant DPP: Create a dedicated DPP descriptor_type = VK_DESCRIPTOR_TYPE_PUSH_CONSTANT, which records the Push Constant Range index, write address, and data size; add the DPP to the DPP queue of the current command buffer for triggering vkCmdPushConstants when subsequent drawing commands are executed.
[0263] S4.4 Root descriptor binding conversion, corresponding to SetGraphicsRootConstantBufferView, etc.
[0264] Taking root CBV binding as an example, the input parameters are parsed as follows: root parameter index RootParameterIndex and CBV GPU virtual address BufferLocation.
[0265] Query the Descriptor Set Layout Set index corresponding to the root parameter from RootSigPipelineLayoutMapping, which is equal to the root parameter index, and bind it to 0.
[0266] Construct root descriptor metadata: parse the Vulkan buffer object corresponding to BufferLocation by querying the buffer mapping table inside VKD3D; construct VkDescriptorBufferInfo: buffer = corresponding VkBuffer, offset = BufferLocation - the GPU base address of the buffer, range = VK_WHOLE_SIZE.
[0267] Write to VHD memory: Calculate the CPU address of the root descriptor in the VHD: vhd_cpu_addr = VHD.cpu_base_addr + root descriptor dedicated partition.offset; Serialize VkDescriptorBufferInfo and write it to vhd_cpu_addr for storage in a fixed format, making it easy for the kernel to read.
[0268] Generate root descriptor (DPP): populate the DPP fields, set_index = root parameter index, descriptor_count = 1, descriptor_type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; add the DPP to the DPP queue of the current command buffer.
[0269] S5, D3D12 drawing and scheduling command conversion.
[0270] S5.1, Interception of drawing and scheduling commands.
[0271] VKD3D intercepts D3D12 drawing and scheduling commands, including DrawInstanced, DrawIndexedInstanced, Dispatch, and DrawInstancedIndirect. It queries the DPP queue corresponding to the current command buffer VkCommandBuffer through the command flow association table: if the queue is empty, it directly translates it into the corresponding Vulkan command (such as vkCmdDraw) and submits it for execution; if the queue is not empty, it processes the DPP queue according to the following steps and then executes the drawing and scheduling commands.
[0272] S5.2, DPP queue parsing and descriptor set acquisition.
[0273] Iterate through each DPP in the DPP queue, including the merged batch DPPs:
[0274] Extract the layout fingerprint of DPP, query the layout fingerprint hash table of LF-LDP, and obtain the corresponding DescriptorSetSubPool sub-pool.
[0275] Sub-pool acquires free descriptor sets:
[0276] Check the free list (LRU) of the sub-pool: If a free descriptor set exists, retrieve the head node of the list (LRU least recently used), mark it as in use, and add it to the list of nodes in use; if the free list is empty, check if the sub-pool descriptor pool has any remaining capacity: call vkGetDescriptorPoolSize to query the remaining capacity; if there is remaining capacity: call vkAllocateDescriptorSets to allocate a new descriptor set, mark it as in use, and add it to the list of nodes in use.
[0277] If the capacity is insufficient: trigger sub-pool expansion, create a new descriptor pool with twice the capacity of the original pool, migrate the in-use descriptor set from the original pool to the new pool, destroy the original pool, and reallocate the new descriptor set.
[0278] Record descriptor set association information: Store the descriptor set, the corresponding DPP, and the command buffer handle into the command buffer and descriptor set association table.
[0279] S5.3 Batch read VHD descriptor metadata.
[0280] Construct the SysVkd3dVhdBatchReadParams system call parameter:
[0281] typedef struct {
[0282] int32_t FD; / / VHD file descriptor
[0283] uint64_t vhd_start_offset; / / Starting offset of the descriptor within the VHD
[0284] uint64_t vhd_end_offset; / / End offset of the descriptor within the VHD
[0285] VkDescriptorType descriptor_type; / / Descriptor type
[0286] uint32_t descriptor_count; / / Number of descriptors
[0287] uint32_t max_write_count; / / Maximum allowed number of VkWriteDescriptorSets (determined by the capacity of the pre-update buffer block)
[0288] int32_t preupdate_buf_count; / / Output: Number of preupdate buffer blocks used
[0289] VhdPreupdateBufferInfo buf_info[8]; / / Output: Array of buffer block information (up to 8 blocks, supports 512 descriptors)
[0290] SysVkd3dVhdBatchReadParams;
[0291] VhdPreupdateBufferInfo is defined as follows:
[0292] typedef struct {
[0293] int32_t buf_idx; / / Buffer block index
[0294] uint64_t cpu_addr; / / Kernel-mode CPU address of the buffer block (accessible from user space)
[0295] uint32_t write_count; / / Number of VkWriteDescriptorSets in this block
[0296] VhdPreupdateBufferInfo;
[0297] Enter the parameters: FD is the FD of VHD, vhd_start_offset, vhd_end_offset, descriptor_type, and descriptor_count are extracted from DPP, max_write_count = 64KB / sizeof(VkWriteDescriptorSet) (approximately 1024);
[0298] The syscall(SYS_vkd3d_vhd_batch_read, ¶ms) is invoked to request the kernel to read descriptor metadata in batches.
[0299] S5.4, Kernel-level batch read and pre-update buffer filling.
[0300] The kernel uses file descriptors (FDs) to locate the VHD core data structure, obtains the spin lock of the handle mapping table, and the lock of the corresponding descriptor type partition.
[0301] Calculate the descriptor traversal step and range: step = VHD.handle_stride; Traversal offset range: current_offset = vhd_start_offset to vhd_end_offset, with a step size of step.
[0302] Pre-update buffer block allocation: acquire the spin lock of the pre-update buffer pool; select free buffer blocks by polling algorithm: current_buf_idx = (last_used_buf_idx + 1) % 16, until a buffer block with a free status is found; mark the buffer block status as occupied, ref_count += 1.
[0303] Descriptor metadata reading and filling: Initialize buffer block write pointer: write_ptr = buf_info[0].cpu_addr; Traverse each current_offset: Query VhdHandleMapNode from the handle mapping table (search by current_offset); Extract descriptor metadata VkDescriptorImageInfo or VkDescriptorBufferInfo;
[0304] Construct the VkWriteDescriptorSet structure:
[0305] sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
[0306] pNext = NULL;
[0307] dstSet = VK_NULL_HANDLE (user-space fill);
[0308] dstBinding = The binding index queried from the root parameter information associated with DPP;
[0309] dstArrayElement = 0;
[0310] descriptorCount = 1;
[0311] descriptorType = descriptor_type;
[0312] pImageInfo = a pointer when the metadata is of image type;
[0313] pBufferInfo = a pointer to the metadata when it is a buffer type;
[0314] pTexelBufferView = NULL;
[0315] Write VkWriteDescriptorSet to the write_ptr position of the buffer block, write_ptr += sizeof(VkWriteDescriptorSet); if the remaining space in the buffer block is insufficient (write_ptr - buf_cpu_addr ≥ 64KB): record the write_count of the current buffer block; allocate the next buffer block and repeat the above steps; record the write_count of the last buffer block.
[0316] Release all spinlocks, fill the output parameters with preupdate_buf_count and buf_info, and return them to VKD3D.
[0317] S5.5, Batch update of descriptor sets.
[0318] S5.5.1, VKD3D receives buffer block information returned by the kernel and iterates through each VhdPreupdateBufferInfo:
[0319] The kernel-mode CPU address of the buffer block is mapped to user space, and user space can directly access it through the VHD's DMA-BUF mmap mechanism. The VkWriteDescriptorSet array in the buffer block is traversed: the dstSet field is filled with the obtained descriptor set, and the binding index of dstBinding is verified to be consistent with the descriptor set layout. A VkWriteDescriptorSet array pointer pWrites is constructed, pointing to the data in the buffer block. vkUpdateDescriptorSets is called: device is the current Vulkan device, writeCount = buf_info.write_count, pWrites = the array pointer in the buffer block, copyCount = 0, pCopies = NULL. Since the buffer block is GPU-visible memory, vkUpdateDescriptorSets only needs to perform driver-level address mapping, eliminating the need for CPU-GPU data copying, significantly reducing overhead.
[0320] S5.5.2 Handling Dynamic Offsets:
[0321] Extract dynamic_offset_count and dynamic_offsets from DPP;
[0322] Call vkCmdSetDescriptorSetDynamicOffset:
[0323] commandBuffer = Current command buffer
[0324] pipelineLayout = the corresponding Pipeline Layout
[0325] firstSet = DPP.set_index
[0326] descriptorSetCount = 1
[0327] pDescriptorSets = Descriptor Sets
[0328] dynamicOffsetCount = dynamic_offset_count
[0329] pDynamicOffsets = dynamic_offsets
[0330] S5.6, Root constant push and descriptor set binding.
[0331] S5.6.1, Handling the root constant DPP.
[0332] Iterate through the root constant DPP in the queue, extracting the Push Constant Range index, VHD CPU address, and data size; read the root constant data from the VHD CPU address and memcpy it into a user-space temporary buffer; call vkCmdPushConstants sequentially according to the split push_constant_ranges order:
[0333] commandBuffer = Current command buffer;
[0334] layout = the corresponding Pipeline Layout;
[0335] stageFlags = range.stageFlags;
[0336] offset = range.offset;
[0337] size = range.size;
[0338] pData = the corresponding data in the temporary buffer;
[0339] S5.6.2, Binding Descriptor Set.
[0340] Call vkCmdBindDescriptorSets:
[0341] commandBuffer = Current command buffer;
[0342] pipelineBindPoint = Graphics / Computation Pipeline (corresponding to D3D12 drawing / scheduling);
[0343] layout = the corresponding Pipeline Layout;
[0344] firstSet = DPP.set_index;
[0345] setCount = the number of DPPs processed;
[0346] pSets = array of descriptor sets;
[0347] dynamicOffsetCount = Total number of dynamic offsets;
[0348] pDynamicOffsets = The merged array of dynamic offsets;
[0349] S5.7 Translation, drawing, and scheduling commands.
[0350] Taking DrawInstanced as an example, convert it to vkCmdDraw:
[0351] vertexCount = VertexCount from the D3D12 command;
[0352] instanceCount = InstanceCount of the D3D12 command;
[0353] firstVertex = FirstVertexLocation of the D3D12 command;
[0354] firstInstance = FirstInstanceLocation of the D3D12 command;
[0355] Call vkCmdDraw(commandBuffer, vertexCount, instanceCount, firstVertex, firstInstance);
[0356] Other commands (such as Dispatch) are transformed according to similar rules to ensure consistent parameter semantics.
[0357] S5.8 Clean up the DPP queue.
[0358] Return the completed DPP to the DPE's DPP memory pool and mark it as free; clear the DPP queue of the current command buffer and wait for the next round of binding commands.
[0359] S6 and D3D12 command list submission interface conversion.
[0360] S6.1, Command Submission Interface Interception.
[0361] When a D3D12 application calls ID3D12CommandQueue::ExecuteCommandLists to submit a list of commands, VKD3D intercepts the call; it parses the input parameters: the array of command lists ppCommandLists and the number of command lists NumCommandLists; it then iterates through each command list, extracting the corresponding Vulkan command buffer VkCommandBuffer and the associated Fence, i.e., ID3D12Fence.
[0362] S6.2, Construct Vulkan commit messages.
[0363] Create a VkSubmitInfo structure for each command buffer:
[0364] sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
[0365] waitSemaphoreCount = 0 (Simplified processing, scalable support);
[0366] pWaitSemaphores = NULL;
[0367] pWaitDstStageMask = NULL;
[0368] commandBufferCount = 1;
[0369] pCommandBuffers = ¤t command buffer;
[0370] signalSemaphoreCount = 0;
[0371] pSignalSemaphores = NULL;
[0372] If the D3D12 application specifies a fence, then the corresponding Vulkan fence (VkFence) is created: construct VkFenceCreateInfo, flags = VK_FENCE_CREATE_SIGNALED_BIT (initial state); call vkCreateFence to create the fence; bind the ID3D12Fence to the VkFence and store it in the D3D12 and Vulkan Fence mapping table.
[0373] S6.3, Binding Descriptor Set Lifecycle and Fence.
[0374] Retrieve all descriptor sets associated with the current command buffer through the command buffer and descriptor set association table; update the reference count and synchronization information for each descriptor set:
[0375] The reference count (ref_count) is incremented by 1 and associated with the current command buffer.
[0376] Record the associated VkFence pointer and command submission timestamp;
[0377] Add the descriptor set to the corresponding sub-pool's linked list and update the LRU linked list sorting.
[0378] S6.4 Submit the Vulkan command.
[0379] Calling vkQueueSubmit: queue is the Vulkan graph / computation queue, submitCount = number of commands in the list, pSubmits = array of VkSubmitInfo, fence = corresponding VkFence; if the D3D12 application requires waiting for a command to complete (such as ID3D12Fence::Wait), it will block and wait for VkFence to be triggered.
[0380] S7, Descriptor set reclamation and VHD partition release.
[0381] S7.1 Asynchronous recycling of descriptor sets.
[0382] S7.1.1, LF-LDP starts a background recycling thread LfLdpRecyclerThread, which performs a recycling check every 100ms: traversing the usage lists of all sub-pools and checking the associated VkFence state of each descriptor set:
[0383] Call vkGetFenceStatus to query the fence status;
[0384] If the fence status is VK_SUCCESS, it means that the GPU has completed execution.
[0385] Descriptor set reference count ref_count -= 1; if ref_count == 0: reset descriptor set contents: call vkUpdateDescriptorSets to set all bindings to default values; move the descriptor set from the in-use list to the free list (LRU tail), and mark the descriptor set status as free.
[0386] S7.1.2 Cleaning up the idle descriptor set of the sub-pool.
[0387] Calculate the idle ratio of the sub-pool: idle_ratio = number of idle pools / total number of allocated pools;
[0388] If idle_ratio > 70% and the number of idle spaces > the number of pre-allocated spaces:
[0389] The excess free descriptor set is selected in LRU order, and the number of free descriptors released = the number of free descriptors - the number of pre-allocated descriptors.
[0390] Construct the vkFreeDescriptorSets parameters: descriptorPool = sub-pool of descriptors, descriptorSetCount = number of descriptors to be released, pDescriptorSets = array of descriptor sets to be released;
[0391] Call vkFreeDescriptorSets to release the descriptor set;
[0392] Update the total number of allocated and idle units in the sub-pool.
[0393] Sub-pool merging check:
[0394] Traverse the layout fingerprint hash table to find sub-pools with the same layout fingerprint; if a duplicate sub-pool is found: migrate the used and idle descriptor sets of the source sub-pool to the target sub-pool; call vkDestroyDescriptorPool to destroy the descriptor pool of the source sub-pool; delete the source sub-pool entry from the layout fingerprint hash table.
[0395] S7.1.2 Handling Synchronous Recycling Triggered by D3D12 Fence Waiting: When the application calls ID3D12Fence::Wait and waits for completion, VKD3D triggers a synchronous recycling callback; it processes all descriptor sets associated with the Fence to ensure timely recycling.
[0396] S7.2, VHD partition release.
[0397] S7.2.1 When a D3D12 application calls ID3D12DescriptorHeap::Release and the reference count is 0: retrieve the VHD partition information corresponding to the heap from the descriptor heap and VHD partition mapping table, including FD, region_id, start_offset, and end_offset; construct the system call parameters sys_vkd3d_vhd_release_region: FD = VHD.FD, region_id = partition ID; and call syscall(SYS_vkd3d_vhd_release_region, ¶ms).
[0398] S7.2.2 Kernel layer handles partition release:
[0399] The kernel uses file descriptors (FDs) to locate the VHD core data structure and acquires a spin lock on the partition management table.
[0400] Find the corresponding VhdRegionNode and set its status to idle;
[0401] Attempt to merge adjacent free partitions: Check if the previous and next nodes of the current partition are free; if so, merge them into one large partition and update the interval tree.
[0402] Release the spinlock on the partition management table;
[0403] Acquire the spinlock of the handle map table, traverse all current_offsets, start_offsets to end_offsets within the partition, with a stride of handle_stride: remove the corresponding VhdHandleMapNode from the handle map table; release the associated Vulkan object reference, such as VkImageView's ref_count -= 1; release the handle map table spinlock.
[0404] S7.2.3, VKD3D Cleanup Mapping Table: Removes the entry for the descriptor heap from the VHD partition mapping table and releases the D3D12DescriptorHeapWrapper wrapper object.
[0405] S8, System Exit and Resource Destruction.
[0406] S8.1, VKD3D process exit triggers resource destruction.
[0407] Destroy LF-LDP resources: Traverse all sub-pools, release descriptor sets in use and idle, and call vkFreeDescriptorSets; Destroy descriptor pool: Call vkDestroyDescriptorPool; Destroy descriptor set layout: Call vkDestroyDescriptorSetLayout.
[0408] Destroy all VkSamplers in the static sampler cache pool: call vkDestroySampler.
[0409] Destroy the layout fingerprint hash table and all sub-pool data structures.
[0410] Destroy DPE resources:
[0411] Release all DescriptorPushPackage objects in the DPP memory pool; destroy the DPP cache queue and command flow association table; close the VHD's DMA-BUF FD.
[0412] Destroy the mapping table set:
[0413] Traverse the root signature and Pipeline Layout mapping table, destroy the corresponding VkPipelineLayout: call vkDestroyPipelineLayout; destroy the resources associated with Push Constant Range; clear all hash tables.
[0414] Call the kernel system call sys_vkd3d_vhd_destroy: pass in the VHD's file descriptor, and request the destruction of the VHD.
[0415] S8.2, Kernel-level VHD destruction.
[0416] The kernel locates the VHD core data structure via file descriptors (FDs):
[0417] Destroy the pre-updated buffer pool: Traverse all buffer blocks, call vkUnmapMemory to unmap, and call vkFreeMemory to release GPU memory;
[0418] Destroy the handle mapping table: Traverse the hash buckets of all partitions and release the VhdHandleMapNode nodes;
[0419] Destroy the partition management table: Release all nodes in the interval tree;
[0420] Unmap VHD memory: Call vkUnmapMemory to unmap the kernel-mode memory, and call unmap_mapping_range to unmap the user-mode memory.
[0421] Release GPU memory: Call vkFreeMemory to release VHD's VkDeviceMemory;
[0422] Disable DMA-BUF inodes and release file descriptors;
[0423] S8.3 Destroy the Vulkan instance.
[0424] Call `vkDestroyFence` to destroy all associated Vulkan fences; call `vkDestroyDevice` to destroy Vulkan devices; call `vkDestroyInstance` to destroy Vulkan instances.
[0425] The following definitions are involved in this embodiment and are explained below:
[0426] 1. Dual-space Virtual Descriptor Heap (VHD)
[0427] Definition: A contiguous block of memory visible to the GPU created by the kernel layer. It achieves the same-source alignment of CPU virtual addresses, kernel virtual addresses, and GPU virtual addresses through memory mapping. It is a core component of the contiguous memory and handle offset management model used to simulate the D3D12 descriptor heap.
[0428] Components: Handle mapping table (segmented hash table, storing the association between descriptor metadata and VHD offset), partition management table (range tree, managing free or allocated partitions), pre-updated buffer pool (16 64KB GPU-visible buffer blocks, used for bulk storage of descriptor metadata);
[0429] Function: Replaces the static model of Vulkan's default descriptor pool and descriptor set, allowing D3D12 descriptor handles to be directly reused as VHD address offsets, avoiding handle translation overhead.
[0430] 2. Descriptor Push Engine (DPE)
[0431] Definition: User-space core module, responsible for recording descriptor information of D3D12 root parameter binding operations, generating descriptor push packets (DPP) and associating them with the Vulkan command buffer, simulating the characteristics of embedded descriptor data in the VK_EXT_push_descriptor command stream;
[0432] Composition: DPP cache queue (circular queue, pre-allocated 1024 DPP objects), command flow association table (hash table, binding command buffers to DPP queue), VHD association information (stores VHD FD, CPU / GPU base address).
[0433] Function: Converts the immediate binding operation of D3D12 into a deferred processing DPP, avoiding the high-frequency overhead of immediate allocation of descriptor sets in traditional schemes.
[0434] 3. Descriptor Push Package (DPP)
[0435] Definition: A lightweight data structure that encapsulates D3D12 descriptor binding information. It is used to temporarily store descriptor metadata in the command stream and process it in batches when a drawing command is triggered.
[0436] Core fields: Magic number (0x44505056, used for command flow validation), Set index, number of descriptors, layout fingerprint (main hash + checksum), VHD offset range, dynamic offset array;
[0437] Function: It replaces the real-time push operation of vkCmdPushDescriptorSetKHR, aggregating scattered descriptor binding information into batch data and reducing the number of subsequent updates.
[0438] 4. Lazy-loaded descriptor set pool LF-LDP driven by layout fingerprint
[0439] Definition: A user-space pooled component that manages descriptor sets by Pipeline Layout fingerprint classification, supporting descriptor set pre-allocation, lazy loading, LRU reuse, and dynamic expansion;
[0440] Composition: Global Manager (including layout fingerprint hash table and global descriptor pool template), Sub-pool (each fingerprint corresponds to a sub-pool, including descriptor pool, idle / used linked list, and pre-allocated quantity), Static Sampler Cache Pool (hash table, storing the association between serialization samplers and VkSampler).
[0441] Function: By accurately matching reusable descriptor sets through layout fingerprints, pre-allocation avoids high-frequency allocation overhead, LRU improves reuse rate, and solves the problems of descriptor set resource exhaustion and fragmentation in traditional solutions.
[0442] 5. Pre-update buffer pool
[0443] Definition: The GPU-visible memory pool in the VHD kernel module, containing 16 64KB buffer blocks, is used to batch store descriptor metadata read from VHD for direct reuse by vkUpdateDescriptorSets;
[0444] Features: Uses a polling algorithm to allocate buffer blocks, supports dual visibility of CPU / GPU, and does not require explicit synchronization after data is written, relying on HOST_COHERENT_BIT;
[0445] Function: Combines multiple vkUpdateDescriptorSets calls into a single batch call, avoiding the CPU overhead of copying descriptor metadata one by one, and achieving performance close to the native performance of VK_EXT_push_descriptor.
[0446] 6. Dynamic layout adaptation layer
[0447] Definition: An intermediate adapter component in the root signature transformation stage, used to resolve compatibility issues between D3D12 root parameter semantics and VulkanPipeline Layout;
[0448] Features: Supports semantic transformation of variable descriptor arrays (mapped to Vulkan dynamic bindings), superscalar root constants (split into multiple Push Constant Ranges), and root descriptors (fixed binding to 0);
[0449] Purpose: To ensure that all characteristics of the D3D12 root signature (such as the UINT_MAX variable array and root constants exceeding 128 bytes) can be losslessly mapped to Vulkan, avoiding the loss of functionality.
[0450] 7. Reference counting mechanism for the three-dimensional association of command buffer, fence, and descriptor set.
[0451] Definition: Descriptor set lifecycle management mechanism, which binds the descriptor set to the commit state of the command buffer and the GPU execution state of the fence through reference counting;
[0452] Logic: When the descriptor set is allocated, the reference count is 1 (associated with the command buffer). When the command is submitted, the reference count is incremented by 1 (associated with the fence). When the fence is triggered, the reference count is decremented by 1. When the reference count is 0, it is added to the free list.
[0453] Purpose: To resolve issues related to shared descriptor sets in multi-command buffers, in-use updates in asynchronous execution scenarios, and dangling references, ensuring the correctness of lifecycle management.
[0454] This invention achieves homo-source mapping of D3D12 descriptor heaps through kernel-level dual-space VHD, replicates the command stream embedded push feature of VK_EXT_push_descriptor through DPE, and implements fine-grained management of descriptor sets through LF-LDP, fully covering all relevant D3D12 interface conversions. Experiments have verified the following technical effects:
[0455] Descriptor update performance is improved by more than 80%, the batch update mechanism reduces the number of vkUpdateDescriptorSets calls by 90%, the pre-update buffer pool avoids CPU-GPU data copying, and the performance is close to that of the native VK_EXT_push_descriptor supported scenarios. The frame rate of high-frequency descriptor switching scenarios is improved by more than 60%.
[0456] Memory fragmentation rate is reduced by 95%. The continuous partition management of VHD and the descriptor set reuse mechanism of LF-LDP completely solve the fragmentation problem caused by frequent allocation / reclamation, reduce memory usage by more than 50%, and ensure no memory leaks during long-term operation.
[0457] It boasts 100% semantic compatibility, fully covering the D3D12 root parameter binding interface series. Descriptor heap handle operations, root signature configuration, and command stream recording processes are completely consistent with native D3D12. It supports edge scenarios such as variable descriptor arrays and superscale root constants, without requiring modification to application code.
[0458] Stability is significantly improved. The three-dimensional associated reference counting mechanism based on command buffer, fence and descriptor set avoids correctness issues such as updates during use and dangling references, reducing the crash rate by more than 99%. It has passed multi-GPU and high-concurrency scenario tests.
[0459] It is highly portable, requiring only modifications to the VKD3D source code and Linux kernel source code, without involving GPU driver modifications. It is compatible with all GPU hardware that supports Vulkan 1.0 and above, and is compatible with mainstream Linux distributions.
[0460] Resource utilization is optimized, and the pre-allocation and elastic expansion strategies balance memory usage and allocation efficiency. The static sampler cache pool reduces the overhead of repeated creation, and the LRU descriptor set reuse mechanism improves the cache hit rate to over 85%.
[0461] This invention fundamentally solves the core contradiction of D3D12-on-Vulkan without VK_EXT_push_descriptor support, enabling D3D12 applications in low-cost GPU hardware, embedded devices and other scenarios.
[0462] 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.
Claims
1. A Direct3D 12 descriptor compatibility method based on address-origin mapping, characterized in that, Specifically, the following steps are included: During VKD3D initialization, the first association information is created, along with a layout fingerprint hash table using fingerprints and checksums as keys and pointers to descriptor set sub-pools as values. The first mapping table between the root signature and pipeline layout, the second mapping table between the descriptor heap and virtual descriptor heap partitions, and the third mapping table between the command buffer and the descriptor set are initialized. A virtual descriptor heap is created, and a homo-source mapping relationship with a fixed offset between CPU and GPU virtual addresses is established based on the same physical memory region. The file descriptor of the virtual descriptor heap, the CPU virtual base address, the GPU virtual base address, and the handle step size are added to the first association information. When a D3D application creates a descriptor heap, VKD3D extracts the descriptor heap type and descriptor handle stride, maps the descriptor heap type to a combination of Vulkan descriptor types, reads the first associated information, and if the handle stride is different from the descriptor handle stride, it reports an error and terminates the process; otherwise, it requests a virtual descriptor heap partition, creates a D3D12 descriptor heap wrapper object and adds it to the second mapping table, maps the CPU and GPU base addresses of the descriptor heap to the address offsets of the virtual descriptor heap partition, and implements the descriptor heap interface to map descriptor copying to direct CPU memory writing; When creating the root signature, the static sampler and root parameters are converted into Vulkan samplers and pipeline layouts respectively, generating a layout fingerprint of the Vulkan pipeline layout. The descriptor set sub-pool corresponding to the layout fingerprint in the layout fingerprint hash table is reused. The root signature, Vulkan pipeline layout, and layout fingerprint are added to the first mapping table. When binding the root parameters, a descriptor push packet is generated according to the root parameter type and added to the descriptor push packet queue of the current command buffer. When performing drawing and scheduling, the drawing and scheduling commands are translated into Vulkan commands and submitted for execution. The Vulkan commands are submitted to the GPU, and the GPU executes the commands to complete the rendering.
2. The Direct3D 12 descriptor compatibility method according to claim 1, characterized in that, The method for generating the layout fingerprint of the Vulkan pipeline layout is as follows: serialize all components of the Vulkan pipeline layout into intermediate data, perform SHA-256 hash on the intermediate data to obtain the master fingerprint, perform CRC32 hash on the intermediate data to obtain the checksum, and the master fingerprint and checksum constitute the layout fingerprint of the Vulkan pipeline layout.
3. The Direct3D 12 descriptor compatibility method according to claim 1, characterized in that, The process of generating a descriptor push package based on the root parameter type when binding the root parameter involves parsing the root parameter index and the descriptor table GPU handle when the root parameter type is a descriptor table. The corresponding virtual descriptor heap partition in the second mapping table is found based on the ID3D12 descriptor heap encapsulation object pointer. The offset of the descriptor table GPU handle in the virtual descriptor heap partition is calculated, the corresponding descriptor set index and the number of descriptors are obtained, and a descriptor push package is generated.
4. The Direct3D 12 descriptor compatibility method according to claim 1, characterized in that, The process of generating a descriptor push packet based on the root parameter type when binding the root parameter involves parsing the root parameter index, constant start offset, constant quantity, and constant data pointer when the root parameter type is a root constant. It also involves querying the VkPushConstantRange information and split mapping corresponding to the root parameter from the first mapping table, calculating the CPU base address of the root constant in the virtual descriptor heap partition, writing the root constant data to the CPU-visible memory of the virtual descriptor heap partition based on the CPU base address, automatically synchronizing the data to the GPU, and generating a descriptor push packet for the root constant.
5. The Direct3D 12 descriptor compatibility method according to claim 1, characterized in that, The process of generating a descriptor push packet based on the root parameter type when binding the root parameter involves parsing the root parameter index and GPU virtual address, querying the descriptor set layout corresponding to the root parameter from the first mapping table, obtaining the Vulkan buffer object corresponding to the GPU virtual address, constructing Vulkan descriptor buffer information using the Vulkan buffer object, offset, and offset range, calculating the CPU address of the root descriptor in the virtual descriptor heap partition, serializing the Vulkan descriptor buffer information, writing it to the CPU address, and generating the descriptor push packet.
6. The Direct3D 12 descriptor compatibility method according to claim 1, characterized in that, Perform batch merging on descriptor push packets in the descriptor push packet queue that have the same layout fingerprint and consecutive descriptor set indices, and the number of such packets exceeds the merging threshold.
7. The Direct3D 12 descriptor compatibility method according to claim 1, characterized in that, The method for translating drawing and scheduling commands into Vulkan commands and submitting them for execution is as follows: If the descriptor push packet queue is empty, it is translated into a Vulkan command and submitted for execution; otherwise, the descriptor push packet queue is traversed to extract the layout fingerprint, the layout fingerprint hash table is queried to obtain the descriptor set sub-pool, the descriptor set, descriptor push packet, and command buffer handle are added to the third mapping table, the virtual descriptor heap is read in batches to update the descriptor set, and the D3D12 command is translated into a Vulkan command and submitted for execution. The method for obtaining the descriptor set sub-pool by querying the layout fingerprint hash table is as follows: If there is an idle descriptor set in the descriptor set sub-pool, the head node of the linked list is taken out, marked as in use, and added to the in-use linked list; otherwise, if the remaining capacity of the descriptor set sub-pool is greater than the threshold, a new descriptor set is allocated, marked as in use, and added to the in-use linked list; if the remaining capacity is not greater than the threshold, a new descriptor pool is created, a new descriptor set is established in it, and the contents of the descriptor set in use are copied to the new descriptor set.
8. The Direct3D 12 descriptor compatibility method according to claim 1, characterized in that, The method of mapping descriptor copying to direct CPU memory writing is as follows: When applying descriptor copying, VKD3D intercepts and parses the source descriptor heap, target descriptor heap, source handle, target handle, and copy count; it obtains the CPU base address of the virtual descriptor heap partition corresponding to the source and target descriptor heaps according to the second mapping table, calculates the CPU address of the source and target descriptors in the virtual descriptor heap, and calculates it as follows: source CPU address = source descriptor heap CPU base address + source handle offset * handle step size, target CPU address = target descriptor heap CPU base address + target handle offset * handle step size; and then directly performs CPU memory copying.
9. The Direct3D 12 descriptor compatibility method according to claim 1, characterized in that, The method for converting root parameters to Vulkan pipeline layouts is as follows: When the root parameter is a descriptor table type, the D3D12 descriptor table range is parsed, and the D3D12 descriptor type is mapped to the corresponding Vulkan type. If it is a variable-size array, a Vulkan dynamic identifier is set and the dynamic offset index is recorded. If it is a fixed-size array, the quantity is assigned, a matching descriptor set is created, a single binding point is configured, and a Vulkan pipeline layout is generated. When the root parameter is a root constant, the root constant parameter is extracted, the total number of bytes is calculated, and the maximum push constant supported by the GPU is queried. If the total number of bytes is less than the maximum push constant, a VkPushConstantRange is created. Otherwise, the root constant is split into multiple VkPushConstantRanges with their addresses concatenated, based on the quotient of the total number of bytes and the maximum push constant. The splitting mapping relationship is recorded, and the VkPushConstantRange array is saved. When the root parameter is a root descriptor, the D3D12 root descriptor type is mapped to a Vulkan type, a VkDescriptorSetLayoutBinding with fixed binding of 0 is created, and a Vulkan pipeline layout is generated.
10. The Direct3D 12 descriptor compatibility method according to claim 1, characterized in that, When the layout fingerprint hash table does not have a descriptor set subpool corresponding to the layout fingerprint, a new descriptor set subpool is created and a descriptor set is pre-allocated, specifically as follows: Calculate the parameters of the descriptor set sub-pool, traverse all descriptor set layouts of the Vulkan pipeline layout, count the total number of descriptors in each layout, reserve expansion space according to multiples of the total to obtain the capacity of a single descriptor set; adjust the pre-allocated quantity according to the capacity of a single descriptor set and a pre-set baseline value, calculate the capacity of the descriptor pool according to the dynamic expansion rules that support a set number of times, and the maximum number of descriptors of different types is the product of the capacity of a single descriptor set and the capacity of the descriptor pool; A Vulkan descriptor pool is constructed based on the descriptor pool capacity, the number of descriptor types, the descriptor pool size array, and the flag indicating whether descriptor sets can be released. The descriptor pool is then stored in the descriptor set sub-pool. Construct allocation information to allocate a specified number of descriptor sets, set all bindings to default values, add them to the free list of the descriptor set sub-pool, and mark the status as free; initialize the descriptor set sub-pool using layout fingerprint, descriptor pool, descriptor set layout array, pre-allocated quantity, current total allocation, free list, in-use list, and recycling threshold, and add it to the layout fingerprint hash table with the layout fingerprint as the key.