A method for virtual implementation of Direct3D stream output based on kernel enhancement

By parsing shader code and building a kernel shared memory pool during the DXVK compilation phase, and establishing a format mapping table and abstraction layer, the problem of Direct3D stream output when the GPU does not support the VK_EXT_transform_feedback extension was solved. This enabled stable and efficient operation of Direct3D stream output on Linux systems, improving frame rate and reducing video memory bandwidth usage.

CN121660867BActive Publication Date: 2026-05-15北京麟卓信息科技有限公司
View PDF 2 Cites 0 Cited by

Patent Information

Authority / Receiving Office
CN · China
Patent Type
Patents(China)
Current Assignee / Owner
北京麟卓信息科技有限公司
Filing Date
2026-02-06
Publication Date
2026-05-15

AI Technical Summary

Technical Problem

Existing technologies suffer from problems such as insufficient parameter parsing accuracy, inadequate state synchronization mechanisms, severe performance loss in multi-path rendering, command queue integration conflicts, and lack of deep collaboration at the kernel layer when the GPU does not support the VK_EXT_transform_feedback extension. This makes it difficult to balance the functional equivalence, performance, and compatibility of Direct3D stream output on Linux systems.

Method used

By parsing shader code during the DXVK compilation phase, constructing a format mapping table and a kernel shared memory pool, establishing an abstraction layer and configuring buffers, intercepting and parsing API calls from Direct3D applications, triggering kernel resource allocation, executing shader rewriting and data distribution, and utilizing Vulkan secondary command buffers to implement streaming output functionality, combined with kernel error mapping and synchronization mechanisms, the stable and efficient operation of streaming output is ensured.

Benefits of technology

It achieves stable and efficient operation of Direct3D stream output on GPUs without VK_EXT_transform_feedback support, ensuring the real-time performance and compatibility of Direct3D applications on Linux systems, improving frame rate and reducing video memory bandwidth usage.

✦ Generated by Eureka AI based on patent content.

Smart Images

  • Figure CN121660867B_ABST
    Figure CN121660867B_ABST
Patent Text Reader

Abstract

The application discloses a kind of based on kernel enhancement's Direct3D stream output virtual implementation method, stream data is built for shader in DXVK compilation stage, the format mapping table of D3D, DXVK and kernel is established with application scene library, shared memory pool is initialized when kernel starts, after DXVK starts, the abstract layer consistent with VK_EXT_transform_feedback is established, parameter is parsed when application calls stream output related API, buffer binding class API triggers kernel to allocate partition for stream, stream control class API triggers corresponding shader rewrite, query operation class API then extracts query related information, data distribution is completed when executing shader and data are written into temporary delivery buffer to meet distribution condition, the distribution path of abstract layer is encapsulated as Vulkan secondary command buffer, and batch submission is completed to GPU to render by multiplexing DXVK rendering thread command pool.
Need to check novelty before this filing date? Find Prior Art

Description

Technical Field

[0001] This invention belongs to the field of computer software development technology, specifically relating to a kernel-enhanced Direct3D stream output virtual implementation method. Background Technology

[0002] DXVK, as an open-source cross-platform graphics interface translation layer, primarily translates Direct3D (D3D 9-11) interface calls on the Windows platform into Vulkan interface calls on the Linux platform, enabling Windows applications to run on Linux systems or Wine. The VK_EXT_transform_feedback extension is the hardware foundation for DXVK to implement the core D3D 10-11 stream output functionality. Through the Vulkan native pipeline architecture, this extension supports directly capturing and writing the output data of the vertex shader (VS) and geometry shader (GS) to a specified buffer. This replicates the core behaviors of D3D Stream Output, such as four independent streams running in parallel, dynamic pausing and resuming, precise vertex and primitive counting, buffer overflow prediction and truncation, and multi-threaded state synchronization. Furthermore, it leverages hardware acceleration to achieve low-latency, high-bandwidth data transmission.

[0003] However, when the GPU does not support the VK_EXT_transform_feedback extension, existing simulation solutions suffer from the following insurmountable technical bottlenecks: First, insufficient parameter parsing accuracy fails to fully cover edge parameter scenarios of the D3D Stream Output API, such as dynamic buffer offsets, non-standard strides, and empty query objects, leading to functional mapping deviations. Second, the state synchronization mechanism is inadequate, and the atomicity of pause and resume cannot be guaranteed. In multi-threaded environments, write offset errors and counter value jumps are prone to occur, resulting in game animation frame errors and missing particle effects. Third, multi-path rendering suffers severe performance degradation. Traditional rendering-to-texture and buffer copying solutions require an additional three to four rendering paths, increasing memory bandwidth usage by 60%. The above points are as follows: First, the frame rate drops by 30% to 100%, which cannot meet the real-time requirements of the application. Second, there are conflicts in command queue integration. The state management and synchronization mechanisms of the simulated Vulkan commands and the original DXVK command queue are not deeply coordinated, which can easily lead to pipeline state disorder and data races. Third, there is a lack of process closure. Key links such as query result write-back and D3D error code mapping are not fully covered, which makes it impossible for D3D applications to obtain correct state feedback. Fourth, there is a lack of deep coordination at the kernel layer. The user-space simulation scheme cannot utilize the GPU scheduling, memory management and atomic operation features of the Linux kernel, resulting in data synchronization delay and excessive command submission overhead.

[0004] In summary, existing technologies do not fully consider the deep coupling between the native functional logic of VK_EXT_transform_feedback and the characteristics of the Linux kernel, and only simulate it through user-space code, making it difficult to balance functional equivalence, performance, and compatibility. Summary of the Invention

[0005] In view of this, the present invention provides a kernel-enhanced virtual implementation method for Direct3D stream output, which enables stable and efficient operation of D3D applications on GPUs without VK_EXT_transform_feedback support without modifying the GPU driver.

[0006] This invention provides a kernel-enhanced Direct3D stream output virtual implementation method, which specifically includes the following steps:

[0007] During the DXVK compilation phase, shader code is parsed, and VS stream data and GS stream data are constructed to describe the stream output attributes of vertex shaders and geometry shaders, respectively. A first mapping table of buffer formats between D3D, DXVK, and kernel is established, and conversion instruction fragments are generated and written to the first mapping table for incompatible formats. Scene parameters are recorded using the scene library for D3D application stream output.

[0008] When the system kernel starts, it allocates the kernel-enhanced shared memory pool, divides it into free partitions of different sizes, and establishes a second mapping table for streams to partitions; after DXVK starts, it establishes a first abstraction layer with data structures and interfaces consistent with the VK_EXT_transform_feedback extension, maps the errors of the first abstraction layer to the extended standard error codes, and then converts them to D3D error codes;

[0009] When a D3D application starts, DXVK sets up the first abstraction layer based on scene parameters obtained from the scene library. When calling the buffer binding API, it parses the buffer parameters, passes the parameters to the kernel to allocate a shared memory pool partition for the stream, and updates the second mapping table. If the formats are incompatible, it saves the conversion instruction fragments obtained from the first mapping table to the second mapping table. When calling the flow control API, it establishes the binding relationship between the query object and the target stream. If a corresponding shader exists, it triggers shader rewriting. When calling the query operation API, it extracts the query parameters, converts the D3D buffer and topology type, and marks the dynamic buffer.

[0010] The execution shader writes the generated VS stream data and GS stream data into the corresponding temporary delivery buffers respectively. The first abstraction layer fills in the UBO according to the second mapping table, binds the compute shader to the UBO, the temporary delivery buffer and the stream partition buffer, and the execution compute shader completes the distribution.

[0011] The distribution path of the first abstraction layer is encapsulated as a Vulkan secondary command buffer. The DXVK rendering thread command pool is reused, and the secondary command buffer is submitted in batches to the DXVK main command queue. The main command queue then submits the commands to the GPU for execution to complete the rendering.

[0012] Furthermore, the VS stream data includes stream index, attribute count, format checksum, vertex ID, and flexible array. The flexible array is used to record attribute offsets and formats, and describes the stream output attributes of the vertex shader, including attribute type, offset in the stream output structure, and corresponding stream index.

[0013] Furthermore, the GS stream data includes the stream index of the vertex, the primitive to which it belongs, the vertex number within the current primitive, and vertex attributes, where vertex attributes include normals, colors, and texture coordinates.

[0014] Furthermore, the free partitions are managed by establishing a free partition linked list, which is established using a doubly linked circular list structure.

[0015] Furthermore, the first abstraction layer fills the UBO according to the second mapping table in the following way: when the usage rate of the temporary delivery buffer is greater than the threshold or the stream output is terminated, the first abstraction layer fills all the dynamic parameters required for the distribution stage into the unified buffer object UBO according to the second mapping table. The dynamic parameters include the number of streams, format conversion instructions and partition addresses. Memory barriers are set to ensure that the compute shader is read after the shader is written.

[0016] Furthermore, when the system kernel starts, it also constructs a flow state control block, which includes state flags, write offsets, number of output vertices, number of output primitives, overflow flags, futex wait queues, associated flow partitions and synchronization locks, and initializes its state flags to idle.

[0017] Furthermore, when the D3D application pauses stream output, the first abstraction layer obtains the current stream state control block. If the state flag is active, the state flag is changed to paused, the stream state control block is locked, and atomic read operations are used to read the current values ​​of the write offset, the number of output vertices, and the number of output primitives from the stream state control block. The current values ​​are stored in the first local variable, the DXVK rendering thread is suspended and added to the kernel futex wait queue; if it is inactive, an error message is returned.

[0018] Furthermore, when the D3D application resumes streaming output, the first abstraction layer obtains the current stream state control block. If the state is marked as paused, the state is changed to active, the stream state control block is locked, the rendering thread in the futex waiting queue is woken up, the stream state control block is unlocked, and a success is returned. If the state is marked as non-paused, an error message is returned.

[0019] Furthermore, when the D3D application obtains stream output statistics or query results, the first abstraction layer obtains the number of vertices and primitives through atomic reads, then maps the obtained kernel-state raw counts to a D3D structure, writes the structure to the buffer specified by the D3D application, and enables the D3D application to obtain the query results.

[0020] Furthermore, when a user-mode memory allocation request is received, a spinlock is used to lock the shared memory pool. The free capacity in the shared memory pool is checked to see if it meets the requested memory size. If it does not, a reserved memory block is enabled, and an error message is returned if the reserved memory is still insufficient. Otherwise, a partition of matching size is searched in the free partition list. If no partition exists, the large partition is split. The partition's data format description and buffer usage flag are initialized, and its lifecycle state is set to allocated but not bound or to be used. The reference count is set to 1. The shared memory pool is unlocked, and the partition ID and user-mode mapping address are returned.

[0021] Beneficial effects:

[0022] This invention identifies stream output-related markers by parsing shader code during the DXVK compilation phase, constructing VS stream data and GS stream data for vertex and geometry shaders respectively, and establishing format mapping tables and application scene libraries for D3D, DXVK, and the kernel. When the kernel starts, it initializes a shared memory pool. After DXVK starts, it establishes an abstraction layer consistent with VK_EXT_transform_feedback, configuring relevant caches and buffers. After the D3D application starts, it adapts scene parameters. When calling stream output-related APIs, DXVK intercepts and parses the parameters, verifies their validity, and buffer binding APIs trigger the kernel to allocate partitions for the stream and record mapping relationships. Flow control APIs trigger the rewriting of corresponding shaders, and query operation APIs extract query-related information. After the shader is executed, the packaged data is written to a temporary delivery buffer. When the distribution conditions are met, parameters are passed through a unified buffer object, and the computation shader is executed to complete data distribution. Finally, the distribution path of the abstraction layer is encapsulated as a Vulkan secondary command buffer, which reuses the DXVK rendering thread command pool to submit commands in batches to the main command queue, and then uniformly submits them to the GPU for rendering. Attached Figure Description

[0023] Figure 1 This is a flowchart illustrating a kernel-enhanced Direct3D stream output virtual implementation method provided by the present invention. Detailed Implementation

[0024] The present invention will be described in detail below with reference to the accompanying drawings and embodiments.

[0025] This invention provides a kernel-enhanced virtual implementation method for Direct3D stream output. Its core idea is as follows: During the DXVK compilation phase, the shader code is parsed to identify stream output-related markers, and VS stream data and GS stream data are constructed for the vertex shader and geometry shader respectively. A format mapping table and application scene library are established for D3D, DXVK, and the kernel. When the kernel starts, a shared memory pool and stream state control block are initialized. After DXVK starts, an abstraction layer consistent with VK_EXT_transform_feedback is established, and related caches and buffers are configured. After the D3D application starts, scene parameters are adapted. When stream output-related APIs are called, DXVK intercepts and parses the parameters, verifies their validity, and buffer binding APIs trigger the kernel to allocate partitions for the stream and record mapping relationships. Stream control APIs trigger the rewriting of corresponding shaders, and query operation APIs extract relevant query information. After the shader is executed, the packaged data is written to a temporary delivery buffer. When the distribution conditions are met, parameters are passed through a unified buffer object, and the computation shader is executed to complete data distribution. Finally, the distribution path of the abstraction layer is encapsulated as a Vulkan secondary command buffer, reusing DXVK. The rendering thread command pool submits commands in batches to the main command queue, and then submits them to the GPU to complete the rendering.

[0026] This invention provides a kernel-enhanced virtual implementation method for Direct3D stream output, the processing flow of which is as follows: Figure 1 As shown, the specific steps include:

[0027] Step 1: During the DXVK compilation phase, the shader code is parsed to identify stream output-related markers, including stream index markers and stream output structure markers.

[0028] For the vertex shader VS, the stream output structure records the type of the stream output semantic related attributes, the offset in the structure, and the corresponding stream index. The VS stream data is constructed to describe the above attributes, including the stream index, the number of attributes, the format check code, the vertex ID, and the flexible array. The flexible array is used to record the attribute offset and format. The packaged VS stream data will be written to the VS temporary delivery buffer.

[0029] For the geometry shader GS, the input topology, output topology, maximum number of output vertices and corresponding flow index are parsed. A global variable GS current vertex count is constructed to record the number of vertices that have been output for the primitive currently being processed. GS flow data is constructed to record the flow index of the vertex, the primitive ID to which it belongs, the vertex index in the current primitive and the vertex attributes. The vertex attributes include normals, colors, texture coordinates, etc. The packaged GS flow data will be written into the GS temporary delivery buffer.

[0030] A three-dimensional mapping table is established between the buffer formats of D3D, DXVK, and the kernel, denoted as the first mapping table. For incompatible formats, SPIR-V format conversion instruction fragments are pre-generated and stored in the first mapping table. A library of usage scenarios related to D3D application stream output is established, recording scenario parameters such as application name, scene name, number of streams, data format, call frequency, priority, and batch processing threshold of D3D applications.

[0031] Specifically, by constructing VS stream data and GS stream data, the native D3D output was converted into structured data that Vulkan can recognize.

[0032] Furthermore, if the GPU only supports SPIR-V 1.0 or 1.1, the OpStore instruction for writing to the storage buffer is replaced with indirect writing via the interface block and fragment shader. This includes: defining the interface block to contain packaged attribute data, writing the output data of the vertex and geometry shaders to the interface block, and having the fragment shader read the data from the interface block and then write it to the texture. Additionally, the rewritten SPIR-V module is cached in the DXVK shader cache. The cache key consists of the hash value of the original SPIR-V, the stream index, and the format checksum, to avoid repeated rewriting and compilation, thus optimizing the shader cache.

[0033] Furthermore, the GS temporary delivery buffer employs a double-buffering mechanism, with one buffer used for writing vertex data for the currently rendered data and the other for distributing compute shaders to avoid read / write conflicts. Furthermore, the size of the GS temporary delivery buffer can be set to twice the product of the maximum number of output vertices and the GS stream data size; for example, the default configuration is 32768 × 64 × 2 = 4 MB.

[0034] Step 2: When the system kernel starts, it uses a large page memory allocation strategy to allocate the kernel-enhanced shared memory pool, divides it into multiple partitions of different sizes, sets the partitions as free partitions, completes the initialization of the shared memory pool, and uses a doubly linked circular list structure to establish a free partition linked list to manage the free partitions.

[0035] Construct a stream state control block. Each stream state control block contains a state flag, write offset, number of output vertices, number of output primitives, overflow flag, futex wait queue, associated stream partition ID, and synchronization lock. Set the state flag to idle in the initial value of the stream state control block. Establish a mapping table from stream to partition, denoted as the second mapping table.

[0036] The shared memory pool includes: a unique identifier, total capacity, allocated capacity, free capacity, stream partition list, spinlock, and reserved memory blocks.

[0037] The second mapping table includes: a unique partition identifier, an associated stream index, a physical memory base address, a kernel-mode virtual address, a user-mode mapped address, a partition size, a used size, a data format description, a buffer usage flag, a lifecycle state, a reference count, and linked list nodes.

[0038] Furthermore, during kernel startup, the shared memory pool is initialized through the shmem_init hook function, and a page-aligned and big-page memory allocation strategy is used to allocate contiguous physical memory blocks of a set size from system memory.

[0039] Furthermore, when a user-mode memory allocation request is received, a spinlock is used to lock the shared memory pool. The free capacity in the shared memory pool is checked to see if it meets the requested memory size. If it does not, a reserved memory block is enabled, and an error message is returned if the reserved memory is still insufficient. Otherwise, a partition of matching size is searched in the free partition list. If no partition exists, the large partition is split. The partition's data format description and buffer usage flag are initialized, and its lifecycle state is set to allocated but not bound or to be used. The reference count is set to 1. The shared memory pool is unlocked, and the partition ID and user-mode mapping address are returned.

[0040] Furthermore, during the streaming output process, when it is detected that the ratio of the number of bytes used in the streaming partition to the total capacity of the partition is greater than a threshold, a new partition is allocated, the data of the original partition is copied to the new partition using dma_memcpy, the binding relationship between the streaming resources and the new partition is updated, the original partition is marked as waiting to be reclaimed, and the kernel garbage collection mechanism is triggered, releasing the memory when the reference count of the original partition is 0.

[0041] When the stream buffer is unbound or the object in the first abstraction layer is destroyed, the partition is reclaimed. Specifically, the partition reference count is atomically decremented. When the reference count is 0, the lifecycle state is set to free and the partition is added to the free partition list. Defragmentation is performed periodically to merge adjacent free partitions.

[0042] Step 3: After DXVK starts, establish a first abstraction layer consistent with the VK_EXT_transform_feedback extension, defining the same core data structures including stream configuration structure, stream output startup structure, stream state structure, and global state structure. Implement equivalent functional interfaces including object creation and destruction interface, flow control interface, and query and counter interface; follow its state machine transition rules, including legal transition paths and illegal transition handling; map the internal errors of the first abstraction layer to the standard error codes of this extension, and then convert them to D3D error codes by the DXVK error mapping module; start the shader buffer pool and the second mapping table, and configure the temporary delivery buffers for the vertex shader and geometry shader.

[0043] Step 4: When the D3D application starts, the D3D buffer loaded by the application is converted into a DXVK buffer handle inside DXVK, and the Vulkan buffer object and memory allocation information are extracted; the scene parameters of the application are obtained from the scene library according to the application name of the D3D application, and the batch processing threshold, distribution priority and temporary delivery buffer size of the first abstraction layer are set according to the scene parameters.

[0044] When a D3D application calls APIs related to stream output, if it is a buffer binding API, it captures the number of streams, the D3D buffer object corresponding to the stream, the buffer's starting offset and data stride, obtains the buffer binding descriptor, extracts the usage type, binding flag, CPU access permissions, size and structure alignment, handles edge scenarios such as the 0xFFFFFFFF default offset and empty buffers, and passes parameters such as stream index, memory requirements and data format to the kernel. The kernel allocates an independent partition for the stream from the shared memory pool and then maps the kernel-mode virtual address to user mode. The mapping relationship between the stream and the partition is recorded in the second mapping table. DXVK updates the memory partition ID, user-mode mapping address and other information in the stream state. If the format is incompatible, it obtains the corresponding SPIR-V format conversion instruction fragment from the first mapping table and saves it to the second mapping table.

[0045] If it is a flow control API, then the primitive topology and overflow prediction query object are parsed, and a binding relationship between the query object and the target flow is established when the overflow prediction query object is not empty, so as to obtain information such as whether to start flow output, the number of target flows, the index of the flow to be started, and the primitive topology; and if there is a shader with a corresponding flow index that is the same as the index of the flow to be started, the shader rewriting is triggered during the shader compilation stage.

[0046] If it is a query operation API, extract the query type, query object, and real-time query flag, as well as the query result buffer, data size, and retrieval flag; verify the obtained stream index, offset, buffer size, and format compatibility; convert the D3D buffer called by the application into a DXVK internal DXVK buffer handle; extract the corresponding Vulkan buffer object and memory allocation information; map the D3D primitive topology to the corresponding Vulkan topology type; and mark the dynamic buffer as needing to be synchronized from CPU to GPU.

[0047] Furthermore, it handles edge scenarios such as the default offset of 0xFFFFFFFF and empty buffers, specifically including: if a stream's buffer is NULL, i.e. unbound, then the stream is marked as idle; if a buffer's starting offset is the default value of D3D 0xFFFFFFFF, then it is resolved to have a buffer starting address of 0; if the stride does not match the buffer structure alignment, then a format warning is recorded and corrected.

[0048] The shader rewriting process includes: when it is a vertex shader, injecting processing logic into the shader to pack vertex attributes into VS stream data according to the format of VS stream data and then write it into the VS temporary delivery buffer; when it is a geometry shader, injecting processing logic into the shader to add a new vertex counter, update the counter after submitting vertex data to the rendering pipeline, and write primitive markers after the vertex output of the current primitive is completed.

[0049] Furthermore, the rewritten shader code is compiled into SPIR-V format, a binding decorator is added, and after adapting to the lower version of SPIR-V, it is cached in the shader cache pool.

[0050] Step 5: Execute the vertex shader and geometry shader to write the generated VS stream data and GS stream data into the corresponding temporary delivery buffers, respectively. When the usage rate of the temporary delivery buffer exceeds the threshold or the stream output terminates, the first abstraction layer fills all the dynamic parameters required for the distribution phase into the unified buffer object (UBO) according to the second mapping table. The dynamic parameters include the number of streams, format conversion instructions, partition addresses, etc. Memory barriers are set to ensure that the compute shader reads the data after the shader has finished writing. The compute shader is bound to the unified buffer object, the temporary delivery buffer, and the stream partition buffer. The number of distribution workgroups is calculated, and the compute shaders are executed in parallel according to the workgroups. The data is written to the partition corresponding to the kernel memory pool to complete the distribution.

[0051] Furthermore, by modifying the kernel DRM module, a GPU memory access trajectory tracking module is added to the Linux kernel to achieve real-time monitoring of the write status of the temporary delivery buffer. Specifically, a write_end hook is added to vm_ops in the DRM driver to record the starting address, size, and timestamp of each write; the buffer utilization rate is calculated, which is the ratio of the written size to the total size. When the utilization rate is greater than the threshold, a kernel interrupt is triggered, and the dispatch thread of the first abstraction layer in user space is woken up through futex_wake to start the dispatch processing of the compute shader.

[0052] Furthermore, to prevent buffer overflow, this invention pre-allocates an emergency buffer and sets its size, for example, to 25% of the temporary delivery buffer. When the main buffer usage exceeds a threshold, the system switches to writing to the emergency buffer and prioritizes processing the data in the emergency buffer during the next distribution to avoid data loss.

[0053] Furthermore, a memory barrier is set synchronously during the distribution process to ensure that subsequent rendering paths can only access the data after the distributed data has been written.

[0054] Step 6: When the D3D application pauses stream output, the first abstraction layer obtains the current stream state control block. If the state flag is active, the state flag is changed to paused, the stream state control block is locked, and atomic read operations are used to read the current values ​​of the write offset, the number of output vertices, and the number of output primitives from the stream state control block. The current values ​​are stored in the first local variable, the DXVK rendering thread is suspended and added to the kernel futex wait queue; if it is inactive, an error message is returned.

[0055] When the D3D application resumes streaming output, the first abstraction layer obtains the current stream state control block. If the state is marked as paused, it changes the state to active, locks the stream state control block, wakes up the rendering thread in the futex waiting queue, unlocks the stream state control block, and returns success. At the end of each frame rendering, the first abstraction layer reads the kernel hardware statistics and compares them with the user-mode counter. If the deviation between the two is greater than a threshold, it uses an atomic addition function to adjust the user-mode counter. If the state is marked as non-paused, it returns an error message.

[0056] When a D3D application retrieves stream output statistics or query results, the first abstraction layer obtains the number of vertices and primitives through atomic reads, then maps the obtained kernel-state raw counts to a D3D structure, writes the structure to a buffer specified by the D3D application, and enables the D3D application to retrieve the query results.

[0057] Step 7: Encapsulate the distribution path of the first abstraction layer into a Vulkan secondary command buffer, reuse the DXVK rendering thread command pool, submit the secondary command buffer to the DXVK main command queue in batches, and then submit it to the GPU for execution in a unified manner by the main command queue to complete the rendering.

[0058] Furthermore, this invention also implements the write-back and error feedback of D3D query results, including: when it is an immediate query, the first abstraction layer directly reads the counter value, fills the D3D query result structure and writes it back, and returns "completed"; when it is a delayed query, the first abstraction layer stores the result in a queue, and when the API call to capture the query result data is a forced refresh flag, refreshes the command queue, waits for the distribution path to complete and the kernel counter calibration to complete, reads the calibrated counter or overflow flag, fills the result structure and writes it back to the application buffer, and returns "completed".

[0059] By establishing a three-dimensional mapping table between kernel error scenarios, Vulkan error codes, and D3D error codes, errors such as memory allocation failures and format incompatibility are converted into error codes that can be recognized by D3D applications.

[0060] Example:

[0061] This embodiment employs a Direct3D streaming output implementation method based on a kernel-enhanced virtual engine provided by this invention. When the GPU does not support the VK_EXT_transform_feedback extension, a kernel-enhanced virtual Transform Feedback engine (KE-VTFE) is constructed through deep extension of the DXVK source code and customized development of the Linux kernel. This achieves accurate, efficient, and unbiased conversion of Direct3D 10 and 11 streaming output functions to the Vulkan interface, ensuring stable operation of the DXVK compatibility layer on GPUs such as AMD GCN 1.0 / 2.0, NVIDIA Kepler series, and Intel Gen7 / 8. It is suitable for cross-hardware adaptation scenarios of Windows applications under Linux systems. The specific process includes:

[0062] KE-VTFE provides full interception and fine-grained parameter parsing of S1 and D3D Stream Output related APIs. Within the DXVK D3D 10 and 11 device and context modules, KE-VTFE constructs a three-layer API interception architecture, including a hook injection layer, a parameter parsing layer, and a semantic verification layer, ensuring full capture of Stream Output related API call behavior and parameter details.

[0063] S1.1, Full interception of API list and layered hook injection.

[0064] The core operation APIs covering the entire lifecycle of stream output include the following three categories: Buffer binding class, which sets the target buffer for stream output (ID3D11DeviceContext::SOSetTargets) and queries the currently bound stream buffer (ID3D11DeviceContext::SOGetTargets); Flow control class, which starts stream output (ID3D11DeviceContext::SOBegin), terminates stream output (ID3D11DeviceContext::SOEnd), pauses stream output (ID3D11DeviceContext::SOPause), and resumes stream output (ID3D11DeviceContext::SOContinue); Query operation class, which retrieves stream output statistics (ID3D11DeviceContext::SOGetStatistics), starts a stream output-related query (ID3D11DeviceContext::BeginQuery), terminates a stream output-related query (ID3D11DeviceContext::EndQuery), and retrieves the query result (ID3D11DeviceContext::GetData).

[0065] By using the existing DXVK hook framework dxvk_hook.h, interception logic is injected at the entry and exit points of API calls: the entry point captures the original parameters, and the exit point processes the return value and synchronizes the state, ensuring the integrity of parameter capture and not interfering with the original logic.

[0066] S1.2, Fine-grained parameter parsing and semantic extraction.

[0067] S1.2.1 Buffer Binding Class SOSetTargets Parameter Parsing. Extract core parameters: number of streams (NumStreams, values ​​from 0 to 4), array of ID3D11Buffer objects for each stream (ppSOTargets), array of buffer start offsets (pOffsets), and array of data strides (pStrides); deeply parse buffer attributes, obtain the descriptor of each buffer (D3D11_BUFFER_DESC) through ID3D11Buffer::GetDesc, and extract the usage type (Usage: D3D11_USAGE_DEFAULT, D3D11_USAGE_DYNAMIC, or D3D11_USAGE_DYNAMIC). D11_USAGE_STAGING), Binding Flags, CPU Access Flags, Size (ByteWidth), StructureByteStride; Edge scene processing: if a first-level buffer in ppSOTargets is NULL (unbound), then the stream is marked as idle; if a certain item in pOffsets is 0xFFFFFFFF (D3D default value), then it is resolved to the buffer start address as 0; if the stride does not match the buffer structure alignment, then a format warning is recorded and automatically corrected later.

[0068] S1.2.2 Flow control class SOBegin parameter parsing. Extract primitive topology (such as D3D11_PRIMITIVE_TOPOLOGY_POINTLIST or TRIANGLELIST) and overflow prediction query object (pPredicationQuery, optional, used to predict whether the buffer overflows); associate the query object with the stream. If pPredicationQuery is not NULL, parse the query object type (D3D11_QUERY_SO_OVERFLOW_PREDICATION), establish the binding relationship between the query object and the target stream, and ensure that the overflow state can be accurately associated with the corresponding stream;

[0069] S1.2.3 Query API Parameter Parsing. For BeginQuery, extract the query type (QueryType: D3D11_QUERY_SO_STATISTICS, D3D11_QUERY_SO_OVERFLOW_PREDICATION), query object (pQuery), and whether it is an immediate query (bImmediate); for GetData, extract the query result buffer (pData), result size (DataSize), and acquisition flags (GetDataFlags: D3D11_ASYNC_GETDATA_DONOTFLUSH or D3D11_ASYNC_GETDATA_FLUSH).

[0070] S1.3, Semantic Validation and Preprocessing.

[0071] S1.3.1, Validity checks include: stream index checks to ensure NumStreams≤4 and a single stream index∈[0,3]; offset checks to ensure that each value of pOffsets is an integer multiple of the GPU memory alignment requirement, which can be obtained by vkGetPhysicalDeviceProperties to ensure offset %minMemoryMapAlignment == 0; buffer size checks to ensure that offset + maximum possible output data size ≤ ByteWidth to avoid overflow during initial binding; format compatibility checks: the buffer format matches the shader output property format, such as D3D's D3DFMT_R32G32B32_FLOAT corresponding to Vulkan's VK_FORMAT_R32G32B32_SFLOAT.

[0072] S1.3.2 Preprocessing Conversion: Convert ID3D11Buffer to a DxvkBuffer handle within DXVK, extract its Vulkan buffer object VkBuffer and memory allocation information DxvkMemory; map the D3D primitive topology to the corresponding Vulkan topology type, such as from D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST to VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; automatically mark the dynamic buffer D3D11_USAGE_DYNAMIC as requiring synchronization from CPU to GPU, and allocate a mappable memory type when binding kernel shared memory later.

[0073] S2. In DXVK, build KE-VTFE to fully align the functions, state machine, and behavioral logic with VK_EXT_transform_feedback, ensuring that the upper-layer D3D translation code of DXVK can be directly called without modification, achieving seamless replacement.

[0074] S2.1, Fully replicate the VK_EXT_transform_feedback core data structure, including:

[0075] S2.1.1 Stream configuration structure, defining VtfStreamCreateInfo, fully aligned with VkTransformFeedbackStreamCreateInfoEXT, containing:

[0076] streamIndex: Stream index, with a value range of [0,3];

[0077] bufferBinding: Vulkan storage buffer binding point, with a value range of [0,3], corresponding one-to-one with the stream index);

[0078] format: Stream data format, VkFormat, which maps one-to-one with D3D format;

[0079] stride: Data stride, expressed in bytes, consistent with pStrides of SOSetTargets;

[0080] offset: The starting offset of the buffer, expressed in bytes, consistent with pOffsets;

[0081] bufferUsage: Buffer usage flags, VkBufferUsageFlags, a replica of VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT;

[0082] S2.1.2, Stream output startup structure, defines VtfBeginInfo, aligns with VkTransformFeedbackBeginInfoEXT, and includes:

[0083] sType: Structure type marker, conforming to the Vulkan structure specification;

[0084] pNext: Extended pointer, reserved for future expansion capabilities;

[0085] transformFeedback: Handle to the KE-VTFE stream output object;

[0086] streamCount: The number of streams started;

[0087] pStreams: A pointer to an array of VtfStreamCreateInfo;

[0088] primitiveTopology: primitive topology, VkPrimitiveTopology;

[0089] predicationQuery: Overflow prediction query object, VkQueryPool, corresponding to pPredicationQuery in D3D;

[0090] predicationResult: The comparison method for prediction results, VkQueryResultFlags;

[0091] S2.1.3, Stream State Structure: Define VtfStreamState, which overrides the stream state dimension of VK_EXT_transform_feedback, and includes:

[0092] state: Stream state, with values ​​of VTF_STREAM_STATE_IDLE, VTF_STREAM_STATE_ACTIVE, and VTF_STREAM_STATE_PAUSED;

[0093] vkBuffer: The handle to the bound Vulkan storage buffer;

[0094] vkDeviceMemory: The device memory handle corresponding to the buffer;

[0095] kernelMemPartitionId: Kernel shared memory partition ID;

[0096] currentWriteOffset: The current write offset, of type uint64_t;

[0097] vertexCount: The number of vertices that have been output, of type uint64_t;

[0098] primitiveCount: The number of primitives that have been output, of type uint64_t;

[0099] overflowFlag: Overflow flag, type bool;

[0100] predicationQueryId: The associated prediction query ID;

[0101] S2.1.4 Global State Structure: Define VtfGlobalState to maintain the global state of KE-VTFE, including:

[0102] streamStates[4]: An array of states for the four streams;

[0103] activeTransformFeedback: The currently active stream output object;

[0104] commandPool: A dedicated command pool, VkCommandPool;

[0105] queryPool: A dedicated query pool, VkQueryPool, used for counters and overflow queries;

[0106] mutex: a multi-threaded state-protected mutex, std::recursive_mutex;

[0107] S2.2 Fully implement the equivalent interface VK_EXT_transform_feedback.

[0108] S2.2.1 Object Creation and Destruction Interfaces. vtfCreateTransformFeedback, corresponding to vkCreateTransformFeedbackEXT, creates a KE-VTFE stream output object and initializes VtfGlobalState and stream state; vtfDestroyTransformFeedback, corresponding to vkDestroyTransformFeedbackEXT, destroys the stream output object and releases the associated command pool, query pool, and kernel memory partition.

[0109] S2.2.2 Flow control interface.

[0110] vtfCmdBeginTransformFeedback: Corresponds to vkCmdBeginTransformFeedbackEXT, which verifies the validity of the flow state, only allows the transition from idle to active, initializes currentWriteOffset, vertexCount, and primitiveCount to 0, overflowFlag to false, and records topology information;

[0111] vtfCmdEndTransformFeedback: Corresponds to vkCmdEndTransformFeedbackEXT, verifies the status, only allows conversion from active to idle, freezes the counter value, and triggers pre-calculation of query results;

[0112] vtfCmdPauseTransformFeedback: Corresponds to vkCmdPauseTransformFeedbackEXT, verifies the status, only allows transitions from active to paused, and locks the current offset and counter through kernel system calls;

[0113] vtfCmdResumeTransformFeedback: Corresponds to vkCmdResumeTransformFeedbackEXT, verifies the status, only allows transitions from paused to active, restores the offset and counter through kernel system calls, and wakes up blocked threads.

[0114] S2.2.3, Query and Counter Interface:

[0115] vtfCmdCopyTransformFeedbackCounter: Corresponds to vkCmdCopyTransformFeedbackCounterEXT, which copies vertexCount / primitiveCount to the query result buffer;

[0116] vtfGetTransformFeedbackStatistics: Corresponds to vkGetTransformFeedbackStatisticsEXT, returns the statistics of the stream, which are mapped to the D3D11_QUERY_DATA_SO_STATISTICS structure of D3D.

[0117] S2.3, State machine transitions and error handling are fully aligned.

[0118] S2.3.1 The state machine strictly follows the VK_EXT_transform_feedback rule:

[0119] Valid flow path: IDLE to ACTIVE (vtfCmdBegin), ACTIVE to PAUSED (vtfCmdPause), PAUSED to ACTIVE (vtfCmdResume), ACTIVE to IDLE (vtfCmdEnd).

[0120] Illegal transition handling: Any illegal state transition (such as PAUSED → IDLE, IDLE → PAUSED) returns VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS, which is mapped to D3D's D3DERR_INVALIDCALL;

[0121] S2.3.2 Error Code Mapping: Internal errors of KE-VTFE, such as memory allocation failures and buffer format incompatibility, are mapped one by one to standard error codes of VK_EXT_transform_feedback, and then converted into D3D error codes by the DXVK error mapping module to ensure that D3D applications can handle errors correctly.

[0122] S3. Precise binding and lifecycle management of streaming resources and the Linux kernel-enhanced shared memory pool. To address the issues of independent storage, dynamic expansion, format adaptation, and efficient reclamation of multi-stream buffers, a dedicated KE-VTFE shared memory pool is created by modifying the Linux kernel's memory management module and DRM subsystem, and full lifecycle binding of streaming resources and the memory pool is achieved.

[0123] S3.1, Deep customization of the Linux kernel-enhanced shared memory pool.

[0124] S3.1.1 Memory Pool Core Structure Design:

[0125] The global memory pool structure vtf_shmem_global_pool includes:

[0126] pool_id: A unique identifier for the memory pool, of type uint32_t;

[0127] total_mem_size: Total capacity of the memory pool, of type uint64_t, defaults to 8GB, and can be dynamically adjusted via / sys / module / vtf_shmem / parameters / total_size;

[0128] allocated_mem_size: Allocated capacity, type uint64_t;

[0129] free_mem_size: Free capacity, type uint64_t;

[0130] stream_partitions: A linked list of stream partitions, struct list_head;

[0131] pool_lock: Spinlock_t, protects atomic operations in a multi-core environment;

[0132] mem_reserve: Reserved memory block, type uint64_t, 10% of the total capacity, for emergency expansion.

[0133] The vtf_stream_partition structure:

[0134] partition_id: A unique identifier for the partition, of type uint32_t;

[0135] stream_index: The index of the associated stream, which is an integer from 0 to 3;

[0136] base_phys_addr: Physical memory base address, phys_addr_t;

[0137] base_virt_addr: Kernel-mode virtual address, void*;

[0138] user_virt_addr: User-space mapping address, void*;

[0139] partition_size: Partition size, type uint64_t;

[0140] used_size: The size already used, of type uint64_t;

[0141] format_desc: Data format description, struct vtf_format_desc, containing vk_format, component_count, and byte_per_component;

[0142] usage_flags: Buffer usage flags, VkBufferUsageFlags;

[0143] lifetime_state: Lifetime state, with values ​​of VTF_PARTITION_ALLOCATED, VTF_PARTITION_BOUND, and VTF_PARTITION_FREE;

[0144] ref_count: Reference count, atomic_t, used for garbage collection;

[0145] link: a linked list node, struct list_head;

[0146] S3.1.2 Implementation of core functions of memory pool.

[0147] The memory pool is initialized during kernel startup by using the shmem_init hook function to initialize vtf_shmem_global_pool. This allocates a contiguous physical memory block of total_mem_size from system memory and employs a page-aligned and HugeTLB (HugeTLB) allocation strategy to reduce TLB miss overhead.

[0148] The partition allocation logic is as follows: When a user-space memory allocation request is received, the following steps are executed: 1. Lock the memory pool with a spinlock; 2. Check if the free capacity meets the requested size. If not, enable reserved memory blocks; 3. If the reserved memory is still insufficient, return VK_ERROR_OUT_OF_DEVICE_MEMORY; 4. Search for a partition of the same size in the free partition list, using the best fit algorithm. If no matching partition is found, split the large partition; 5. Initialize the partition's format_desc and usage_flags, set lifetime_state to VTF_PARTITION_ALLOCATED, and set the reference count to 1; 6. Unlock the memory pool and return the partition ID and the user-space mapping address.

[0149] The partition expansion logic is as follows: when the used_size is detected to be close to the partition_size during the stream output process, and the threshold is 80%, expansion is automatically triggered: allocate a new, larger partition, which is 1.5 times the original size; copy the data of the original partition to the new partition through dma_memcpy, with direct copying in kernel mode to avoid CPU involvement; update the binding relationship between the stream resources and the new partition, and mark the original partition as waiting to be reclaimed; trigger the kernel garbage collection mechanism, and release the memory when the reference count of the original partition is 0.

[0150] The partition reclamation logic is as follows: when the stream buffer is unbound (i.e., SOGetTargets is set to NULL or the KE-VTFE object is destroyed), the following actions are performed: atomically decrement the partition reference count; if the reference count is 0, set lifetime_state to VTF_PARTITION_FREE and add the partition to the free list; use a kernel timer with a period of 1 second to periodically perform defragmentation and merge adjacent free partitions.

[0151] S3.2, Binding process of stream resources and memory pool.

[0152] User-space binding trigger: After KE-VTFE resolves the buffer binding request of SOSetTargets through step 1, it calls the dxvk_vtfe_mem_bind function, passing the following parameters: stream index, memory requirements of DxvkBuffer (size, alignment, usage flags), and data format.

[0153] Kernel-mode binding processing: User space adds a new system call `sys_vtf_shmem_bind`, which can be done by modifying `kernel / sys.c` and `arch / x86 / kernel / syscall_table_64.S` to add the system call number and pass the binding parameters to the kernel. The kernel `vtf_shmem_bind` function performs a check on the validity of the stream index, calls the memory pool allocation logic to allocate an independent partition for the stream, and maps the kernel-mode virtual address of the partition to the user-mode address space of the DXVK process. This can be achieved through `vm_mmap` with permissions `PROT_READ|PROT_WRITE`. The mapping relationship between the stream index and the partition ID is recorded and stored in the `stream_index` field of `vtf_stream_partition`.

[0154] User-space metadata synchronization: KE-VTFE updates information such as kernelMemPartitionId, vkBuffer, and vkDeviceMemory in VtfStreamState; it maintains the stream-to-partition metadata mapping table VtfStreamMemMap, which records the user-space mapping address, format description, expansion threshold, etc. of the partition for subsequent access by shaders and Compute Shaders.

[0155] S3.3, Buffer format adaptation depth processing.

[0156] Format mapping table construction: KE-VTFE predefines three-dimensional mapping tables describing D3D format, Vulkan format, and kernel format, covering all Stream Output formats supported by D3D 10 / 11, such as D3DFMT_X8R8G8B8, VK_FORMAT_B8G8R8A8_UNORM, and component_count=4, byte_per_component=1.

[0157] Format conversion instruction generation: If there is a difference between the D3D format and the format supported by the Vulkan storage buffer, such as D3DFMT_PACKED_NORMAL, then the following steps are performed: Extract format conversion rules from the mapping table, such as the unpacking algorithm for converting 16-bit packed normals to 32-bit floating-point normals; generate SPIR-V format conversion instruction fragments, such as instruction fragments composed of OpBitwiseAnd, OpShiftRightLogical, and OpConvertUToF; cache the conversion instruction fragments in the format_conv_cache field of VtfStreamMemMap, and then embed them into the distributed Compute Shader.

[0158] Byte order adaptation: For a very small number of big-endian GPUs, add the byte order reversal instruction OpBitReverse to the conversion instructions to ensure that the data storage order is consistent with D3D.

[0159] Fine-grained rewriting of S4 and HLSL shaders and injection of Vulkan-compatible code. To accurately convert the output logic of D3D's VS and GS into a Vulkan-capable form, the DXVK HLSL to SPIR-V compilation module is deeply extended to achieve automated and unbiased rewriting of shaders.

[0160] S4.1 Shader type identification and rewrite triggering mechanism.

[0161] The static identification process, during the HLSL code parsing phase, such as the execution phase of the parseShader function, checks for the presence of semantic tags related to Stream Output, including:

[0162] Stream index marker: [stream(index)], such as [stream(0)] float4 pos : SV_POSITION;

[0163] Stream output structure markers: struct StreamOutput { ...} or output StreamOutput so.

[0164] The dynamic triggering method for shader rewriting is as follows: based on the parsed SOBegin parameter, it can be determined whether the stream output has been started. During the shader compilation stage, such as the execution stage of the compileShader function, the rewriting logic is dynamically triggered. If a stream is in a pending state and the shader contains the output semantics of that stream, then the rewriting is executed.

[0165] S4.2, VS fine-grained rewriting and data delivery code injection.

[0166] VS Output Structure Parsing: Parse the output structure of VS, such as struct VS_OUT, and extract all attributes with StreamOutput semantics, such as position pos, normal, and color;

[0167] Record the type (e.g., float3 or int2), offset (byte offset in the structure), and corresponding stream index for each attribute.

[0168] Data Packaging Protocol Definition: A unified packaging structure for VtfVsStreamData is designed to ensure cross-GPU compatibility. The structure is as follows:

[0169] struct VtfVsStreamData {

[0170] uint32_t streamIndex; / / Stream index, an integer from 0 to 3.

[0171] uint32_t attrCount; / / Number of attributes

[0172] uint32_t formatChecksum; / / Format checksum, a CRC32 value calculated from the format description.

[0173] uint32_t vertexId; / / Vertex ID, used for counter calibration

[0174] uint8_t attrData[]; / / Flexible array, storing attribute data arranged in a formatted manner.

[0175] };

[0176] The code injection process injects the following logic (HLSL form, subsequently compiled to SPIR-V) before the VS main function returns: Initialize the VtfVsStreamData structure, filling it with the current stream index streamIndex and the number of attributes attrCount; calculate the formatChecksum, calculating the CRC32 value for the attribute format description for format verification during subsequent distribution. The attribute format description consists of type, offset, and stream index; write the attribute data into the attrData flexible array according to the attribute offset and format, such as float3 pos stored as 32-bit floating-point, occupying 12 bytes; write the packaged structure into the VS temporary delivery buffer VtfVsDispatchBuffer, binding it to a StorageBuffer with Binding=0.

[0177] Byte alignment is handled according to the GPU's storage buffer alignment requirements. The minStorageBufferOffsetAlignment can be obtained through vkGetPhysicalDeviceProperties, and attrData is padded with byte alignment. If it is less than 8 bytes, zeros are added. A padding field is added to the end of the structure to ensure that the size of the entire structure is an integer multiple of the alignment requirements.

[0178] S4.3, GS fine-grained rewrite and variable vertex output capture.

[0179] GS core parameter parsing: parsing the input topology [inputassembler(inputprimitive=pointlist)] and output topology [outputtopology(trianglelist)] of GS; parsing the maximum number of output vertices [maxvertexcount(32)] and the stream index marker such as [stream(1)]; identifying the EmitVertex() and EndPrimitive() function call positions, which are the key nodes for variable vertex output;

[0180] Variable vertex capture logic injection: Add a new global variable `uint32_tcurrentPrimitiveVertexCount` in GS to record the number of vertices that have been output by the current primitive; inject the following content after the `EmitVertex()` call:

[0181] currentPrimitiveVertexCount++;

[0182] / / Record the attribute data of the current vertex

[0183] VtfGsStreamData soData;

[0184] soData.streamIndex = 1; / / Current stream index

[0185] soData.primitiveId = gl_PrimitiveID;

[0186] soData.vertexCountPerPrimitive = currentPrimitiveVertexCount;

[0187] soData.attrData = packAttributes(normal, color, texCoord); / / Custom packing function

[0188] / / Write to GS temporary delivery buffer

[0189] vtfGsDispatchBuffer[vtfGsDispatchBufferWriteIndex++] = soData;

[0190] Inject the following after the EndPrimitive() call:

[0191] / / Write primitive vertex count flags

[0192] VtfGsPrimitiveMarker marker;

[0193] marker.primitiveId = gl_PrimitiveID;

[0194] marker.totalVertexCount = currentPrimitiveVertexCount;

[0195] vtfGsMarkerBuffer[vtfGsMarkerBufferWriteIndex++] = marker;

[0196] / / Reset vertex counter

[0197] currentPrimitiveVertexCount = 0;

[0198] The GS temporary delivery buffer adopts a double buffering mechanism, that is: one buffer is used for writing vertex data of the current Draw Call, and the other is used for Compute Shader distribution to avoid read and write conflicts. The buffer size is equal to the maximum number of output vertices × VtfGsStreamData size × 2 (double buffering), and the default configuration is 32768 × 64 × 2 = 4MB.

[0199] S4.4 and SPIR-V code optimization and compatibility adjustments.

[0200] When compiling the rewritten HLSL code to SPIR-V, add the necessary decorators:

[0201] Bind the delivery buffer to DescriptorSet=0, Binding=0, and add the VkDescriptorSetLayout binding description VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;

[0202] Add the VkDecorationBuiltIn decorator to system values ​​such as vertex ID and primitive ID, for example, gl_PrimitiveID→OpDecorate %gl_PrimitiveID BuiltIn PrimitiveId.

[0203] For older versions of SPIR-V, if the GPU only supports SPIR-V 1.0 / 1.1, the apiVersion can be detected via vkGetPhysicalDeviceProperties2. The OpStore instruction for writing to the storage buffer can be replaced with indirect writing via the interface block and fragment shader. This includes: defining an interface block VtfStreamInterfaceBlock containing the packaged attribute data; writing the output data of VS and GS to the interface block; and reading the interface block data through the Fragment Shader and writing it to the texture VkImage.

[0204] Shader cache optimization: The rewritten SPIR-V module is cached in the DXVK shader cache. The cache key is the original SPIR-V hash, stream index and format checksum, avoiding repeated rewriting and compilation.

[0205] S5. Refined Implementation of the Two-Stage Data Capture Mechanism. By designing a two-stage mechanism for shader delivery to Compute Shader distribution, the shader output capture logic of VK_EXT_transform_feedback is accurately replicated. At the same time, overhead is reduced through Linux kernel memory access optimization and synchronization mechanisms.

[0206] S5.1 Refined management of temporary delivery buffer zones.

[0207] During KE-VTFE initialization, dedicated delivery buffers VtfVsDispatchBuffer and VtfGsDispatchBuffer for VS and GS are created and configured as follows: usage flags are VK_BUFFER_USAGE_STORAGE_BUFFER_BIT and VK_BUFFER_USAGE_TRANSFER_SRC_BIT; memory attribute is VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, using device local memory to reduce CPU intervention; allocation is done through the DXVK memory allocator, prioritizing the use of large page memory.

[0208] Buffer status monitoring: By modifying the kernel DRM module, a GPU memory access trajectory tracking module is added to the Linux kernel to monitor the write status of the delivery buffer in real time. Specifically, a write_end hook is added to vm_ops in the DRM driver to record the starting address, size, and timestamp of each write; the buffer utilization rate is calculated, and when the utilization rate reaches 75%, the kernel interrupt tasklet_schedule is triggered.

[0209] The kernel interrupt handling involves waking up the distribution thread of the user-space KE-VTFE via futex_wake and starting the Compute Shader distribution process.

[0210] Buffer overflow protection: A pre-allocated emergency buffer of 25% of the delivery buffer size is used. When the main buffer usage reaches 100%, the system automatically switches to writing to the emergency buffer. Data in the emergency buffer is processed first during the next distribution to avoid data loss.

[0211] S5.2 In-depth design and dynamic configuration of the Compute Shader for streaming data distribution.

[0212] The core functions of the Compute Shader, denoted as VtfDispatchCS, include: reading the packaged data from the temporary delivery buffer, verifying the formatChecksum to ensure format consistency, performing format conversion, writing attribute data to the corresponding stream partition in the kernel shared memory pool by stride, and updating the stream's vertex and primitive counters.

[0213] Dynamic configuration is achieved by passing dynamic parameters through a UBO (Uniform Buffer Object) VtfDispatchUBO. The structure of VtfDispatchUBO is as follows:

[0214] struct VtfDispatchUBO {

[0215] uint32_t streamCount; / / Number of streams

[0216] uint32_t formatConvEnable[4]; / / Format conversion enable flag for each stream

[0217] uint32_t stride[4]; / / stride of each stream

[0218] uint32_t offset[4]; / / Current write offset for each stream

[0219] uint64_t streamPartitionAddr[4]; / / Kernel partition user-space address for each stream

[0220] uint32_t formatConvInstructions[4]

[32] ; / / Format conversion instructions (SPIR-V opcodes)

[0221] };

[0222] Before distribution, KE-VTFE populates the VtfDispatchUBO with the flow-to-partition metadata mapping table, eliminating the need to recompile shaders for different flows or formats.

[0223] Multi-stream parallel distribution optimization: A workgroup design based on stream grouping is adopted, with each workgroup processing a batch of data from one stream. By default, each workgroup processes 64 vertices. Multi-stream parallel distribution is achieved by indexing streams using gl_WorkGroupID.x and vertices using gl_LocalInvocationID.x.

[0224] S5.3, Refined Data Distribution Process.

[0225] S5.3.1. Distribution triggering conditions: Any one of the following must be met: the temporary delivery buffer usage rate reaches the threshold (75%), D3D calls SOEnd (terminates stream output), subsequent rendering passes require Stream Output data (triggered by the DXVK pipeline dependency detection module), or the emergency buffer usage rate reaches 50%.

[0226] S5.3.2 pre-distribution synchronization preparation includes: KE-VTFE calling vkCmdPipelineBarrier to set a memory barrier.

[0227] Source stage: VK_PIPELINE_STAGE_VERTEX_SHADER_BIT or VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT;

[0228] Source access: VK_ACCESS_SHADER_WRITE_BIT;

[0229] Target phase: VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;

[0230] Target access: VK_ACCESS_SHADER_READ_BIT;

[0231] Ensure that the Compute Shader reads data only after the shader has finished writing.

[0232] S5.3.3, Compute Shader execution:

[0233] Call vkCmdBindDescriptorSets to bind VtfDispatchUBO, delivery buffer, and stream partition buffer; calculate the number of workgroups to be distributed as workGroupCount = (number of vertices to be distributed + workgroup size - 1) / workgroup size; call vkCmdDispatch to execute the distribution of Compute Shader.

[0234] S5.3.4. Post-distribution synchronization processing:

[0235] Call vkCmdPipelineBarrier again to set the memory barrier:

[0236] Source phase: VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;

[0237] Source access: VK_ACCESS_SHADER_WRITE_BIT;

[0238] Target phases: VK_PIPELINE_STAGE_VERTEX_INPUT_BIT and VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;

[0239] Target accesses: VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT and VK_ACCESS_SHADER_READ_BIT;

[0240] Ensure that the distributed data is written before subsequent rendering passes access it.

[0241] S5.3.5, Enhanced Kernel Layer Synchronization:

[0242] After distribution, KE-VTFE adds a new system call sys_vtf_memory_barrier, which can modify kernel / sys.c to trigger the kernel-mode GPU memory barrier; the kernel calls the drm_gpu_synchronize function of the DRM driver to ensure that the GPU has completed writing data and avoid CPU-GPU synchronization delay.

[0243] S6. Fine-grained implementation of pause and resume operations and counter synchronization based on Linux kernel atomic operations. To address the insufficient atomicity of pause and resume operations and the accuracy deviation of the counter in user-space simulation, the atomic operation module, futex subsystem, and DRM statistics module of the Linux kernel are modified to achieve state control and counting functions equivalent to VK_EXT_transform_feedback.

[0244] S6.1 Kernel-mode implementation of the flow state control block.

[0245] Define the core structure:

[0246] struct vtf_stream_control_block {

[0247] / / Status flags ensure multi-threaded visibility for atomic variables

[0248] atomic_t state; / / 0=IDLE, 1=ACTIVE, 2=PAUSED

[0249] / / Write offset, a 64-bit atomic variable, supporting lock-free read and write.

[0250] atomic64_t write_offset; / / Current write offset

[0251] / / Counter, a 64-bit atomic variable, precisely records the number of outputs.

[0252] atomic64_t vertex_count; / / Number of vertices already output

[0253] atomic64_t primitive_count; / / Number of primitives already output

[0254] / / Overflow flag, an atomic boolean value

[0255] atomic_t overflow_flag; / / 0 = no overflow, 1 = overflow

[0256] / / Futex wait queue, used to block threads when paused.

[0257] struct futex_q pause_waitq; / / Waiting queue node

[0258] / / Associated stream partition ID

[0259] uint32_t partition_id; / / Kernel shared memory partition ID

[0260] / / Synchronization locks are used to protect operations in the waiting queue.

[0261] spinlock_t lock; / / Spinlock

[0262] };

[0263] Global control block management: The kernel maintains the vtf_stream_control_blocks[4] array corresponding to 4 streams, which is initialized when the system starts. The initial state of each control block is IDLE (0).

[0264] S6.2, Atomized implementation of the pause mechanism, corresponding to SOPause mapping to vtfCmdPauseTransformFeedback.

[0265] S6.2.1 User-mode triggering process.

[0266] D3D calls SOPause, the DXVK translation layer calls vtfCmdPauseTransformFeedback; KE-VTFE verifies the stream status, requiring the status to be ACTIVE, and returns an error if it is invalid; the newly added system call sys_vtf_atomic_pause is called, passing the stream index parameter.

[0267] S6.2.2, Kernel-state atomic operations.

[0268] long sys_vtf_atomic_pause(uint32_t stream_index) {

[0269] struct vtf_stream_control_block *cb = &vtf_stream_control_blocks[stream_index];

[0270] int old_state, new_state;

[0271] / / Step 1: Atomic check and update state (ACTIVE→PAUSED)

[0272] do {

[0273] old_state = atomic_read(&cb->state);

[0274] if (old_state != 1) / / If not in ACTIVE state, return an error.

[0275] return -EINVAL;

[0276] new_state = 2; / / PAUSED

[0277] } while (!atomic_cmpxchg(&cb->state, old_state, new_state));

[0278] / / Step 2: Lock the control block to prevent concurrent modifications

[0279] spin_lock(&cb->lock);

[0280] / / Step 3: Record the current offset and counter value (for verification during recovery)

[0281] uint64_t current_offset = atomic64_read(&cb->write_offset);

[0282] uint64_t current_vtx = atomic64_read(&cb->vertex_count);

[0283] uint64_t current_prim = atomic64_read(&cb->primitive_count);

[0284] / / Step 4: Block the current thread (DXVK rendering thread)

[0285] futex_wait_setup(&cb->pause_waitq, &cb->state, 2, &cb->lock);

[0286] spin_unlock(&cb->lock);

[0287] / / Step 5: After the thread is awakened, return success.

[0288] return 0;

[0289] }

[0290] S6.2.3 Key safeguards: atomic_cmpxchg is used for atomic comparison and swapping to ensure the atomicity of state transitions and avoid multiple threads modifying the state simultaneously; the rendering thread is blocked by the futex waiting queue to ensure that no data is written during the pause and to avoid the offset and counter from being confused.

[0291] S6.3, Atomized implementation of the recovery mechanism, corresponding to SOContinue mapping to vtfCmdResumeTransformFeedback.

[0292] S6.3.1, User-mode trigger.

[0293] D3D calls SOContinue, the DXVK translation layer calls vtfCmdResumeTransformFeedback; KE-VTFE verifies the stream status, requiring the status to be PAUSED, and returns an error if it is invalid; the newly added system call sys_vtf_atomic_resume is called to pass the stream index parameter.

[0294] S6.3.2, Kernel-state atomic operations.

[0295] long sys_vtf_atomic_resume(uint32_t stream_index) {

[0296] struct vtf_stream_control_block *cb = &vtf_stream_control_blocks[stream_index];

[0297] int old_state, new_state;

[0298] / / Step 1: Atomic check and update state (PAUSED→ACTIVE)

[0299] do {

[0300] old_state = atomic_read(&cb->state);

[0301] if (old_state != 2) / / If not in a PAUSED state, return an error.

[0302] return -EINVAL;

[0303] new_state = 1; / / ACTIVE

[0304] } while (!atomic_cmpxchg(&cb->state, old_state, new_state));

[0305] / / Step 2: Lock the control block and wake up the waiting thread

[0306] spin_lock(&cb->lock);

[0307] / / Step 3: Wake up the rendering thread that is blocked in pause_waitq

[0308] futex_wake(&cb->pause_waitq, 1); / / Wakes up one thread (ensuring single-threaded resumption)

[0309] / / Step 4: Unlock the control block

[0310] spin_unlock(&cb->lock);

[0311] / / Step 5: Return success

[0312] return 0;

[0313] }

[0314] S6.3.3 Key safeguards: During recovery, only one blocked thread is woken up to avoid data races caused by multiple threads writing at the same time; state transitions are still implemented through atomic_cmpxchg to ensure atomicity.

[0315] S6.4 Nanosecond-level synchronization and calibration of the counter.

[0316] S6.4.1 Real-time statistics at the kernel level.

[0317] Modify the vblank processing module of DRM, adding a new vtf_counter_stats structure to record statistical information for each stream, including:

[0318] struct vtf_counter_stats {

[0319] uint64_t vertex_count_hw; / / Number of vertices counted by GPU hardware

[0320] uint64_t primitive_count_hw; / / Number of primitives counted by GPU hardware

[0321] uint64_t write_bytes_hw; / / Number of bytes written, as measured by GPU hardware

[0322] uint64_t last_sync_time; / / Last synchronization timestamp (nanoseconds)

[0323] };

[0324] In the drm_mode_page_flip callback of the DRM driver, the actual number of vertices and primitives in the output are read through the GPU hardware counters (such as AMD's PMC and NVIDIA's NVPMU), and vtf_counter_stats is updated.

[0325] The statistical period is 1 millisecond (triggered by the DRM's vblank timer) to ensure statistical accuracy.

[0326] S6.4.2, Counter calibration from user mode to kernel mode.

[0327] At the end of each frame of KE-VTFE, when the flushCommandList function is executed, the kernel vtf_counter_stats are read via ioctl call to DRM_IOCTL_VTF_COUNTER_SYNC;

[0328] The calibration logic is executed, including: calculating the difference delta between the user-space counter atomic64_read(&cb->vertex_count) and the kernel hardware statistic vertex_count_hw; if the absolute value of delta is not greater than 1, no adjustment is made; otherwise, the user-space counter is atomically adjusted using atomic64_add(delta, &cb->vertex_count); and recording the calibration log for subsequent optimization.

[0329] S6.4.3 Generation of Counter Query Results. When D3D calls SOGetStatistics or GetData, KE-VTFE performs the following: reads vertex_count and primitive_count using atomic64_read, converts them into D3D's D3D11_QUERY_DATA_SO_STATISTICS structure NumPrimitivesWritten=primitive_count, NumVerticesWritten=vertex_count; and writes the results to the query result buffer specified by D3D.

[0330] S6.5 Overflow detection and feedback.

[0331] S6.5.1 Kernel Layer Overflow Detection: The kernel vtf_shmem_monitor thread (period 100 microseconds) checks whether the used_size of each stream partition exceeds the partition_size; if it does, it atomically sets the overflow_flag of vtf_stream_control_block to 1.

[0332] S6.5.2 User-space overflow feedback: KE-VTFE reads overflow_flag in vtfGetTransformFeedbackStatistics and maps it to the D3D11_QUERY_DATA_SO_OVERFLOW_PREDICATION result of D3D. If an overflow occurs, the data truncation logic is triggered, which discards the part exceeding partition_size and only retains the first partition_size / stride vertices. The result of the query object is updated synchronously to ensure that the D3D application can obtain the overflow status.

[0333] S7. Multi-pass pipeline optimization and deep integration with command queues. To reduce the performance loss of multi-pass rendering, the KE-VTFE distribution path is deeply integrated with the original Vulkan command queue and state management system of DXVK, and execution efficiency is improved through Linux kernel scheduling optimization.

[0334] S7.1 Batch processing optimization and intelligent scheduling for Pass distribution.

[0335] S7.1.1, Refined design of batch processing controller.

[0336] A new VtfBatchDispatcher module has been added to maintain a queue of data to be dispatched, recording the number of vertices to be dispatched, the starting address of the data, and the priority of each stream. Priorities are divided according to the stream's call frequency and subsequent rendering dependencies, with streams that have urgent dependencies being dispatched first. The call frequency includes high, medium, and low.

[0337] S7.1.2 Dynamic batch processing threshold adjustment.

[0338] The default base threshold is 16KB.

[0339] The dynamic adjustment logic is as follows: Calculate the optimal threshold using the peak write rate (write_rate) from the kernel's vtf_buffer_stats: optimal_threshold = min(16KB, max(4KB, write_rate * 5ms)); If the write rate of a certain stream exceeds 50% of the GPU memory bandwidth, lower the threshold to 8KB to reduce the amount of data distributed in a single operation; if the write rate is below 10%, increase the threshold to 32KB to reduce the number of distributions.

[0340] The threshold adjustment is implemented through the sys_vtf_set_batch_threshold system call and synchronized to the user-space VtfBatchDispatcher in real time.

[0341] S7.1.3 Batch merging strategy.

[0342] Multiple stream dispatch requests within the same Draw Call are merged into a single Compute Shader call and processed through multi-stream parallel dispatch. Stream dispatch requests of the same priority from adjacent Draw Calls are merged into a single dispatch if the time interval is no greater than 1ms, reducing command submission overhead.

[0343] S7.2 Deep integration and synchronization of command queues.

[0344] S7.2.1 Fine-grained management of secondary command buffers.

[0345] The KE-VTFE dispatch passes, such as Compute Shader calls and memory barriers, are encapsulated into Vulkan secondary command buffers, VkSecondaryCommandBuffer, configured as follows: usage flags are VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT and VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT; the command pool reuses the DXVK rendering thread command pool to avoid additional creation; the lifecycle of the secondary command buffer is bound to the DrawCall, with one Draw Call corresponding to one or more secondary command buffers, determined by the number of dispatches.

[0346] S7.2.2 Synchronization mechanism for multi-threaded command submission.

[0347] DXVK supports multi-threaded command submission, including the main thread, rendering thread, and Compute thread. To avoid contention in the secondary command buffer, a command pool isolation strategy is adopted. Specifically, an independent secondary command pool (VtfSecondaryCmdPool) is created for KE-VTFE, which is isolated from the command pools of other threads. The creation, recording, and execution of the secondary command buffer are protected by a mutex lock (std::mutex).

[0348] Command submission order guarantee: When recording the secondary command buffer, it is recorded in the order of shader delivery, memory barrier, and Compute Shader dispatch to memory barrier; during execution, the secondary command buffer is submitted to the DXVK main command queue VkQueue and executed in batches through vkCmdExecuteCommands.

[0349] S7.2.3 Precise synchronization of semaphores and fences.

[0350] The dispatch pass is synchronized with the preceding shader pass: a semaphore vtfPreDispatchSemaphore is created, which is triggered after the preceding shader pass is executed; the dispatch pass waits for this semaphore before executing.

[0351] The dispatch pass is synchronized with the subsequent rendering pass: a semaphore vtfPostDispatchSemaphore is created, which is triggered after the dispatch pass is executed; the subsequent rendering pass waits for this semaphore before it is executed.

[0352] Cross-frame synchronization: Use a fence (VkFence) to ensure that the dispatch command for the current frame is recorded only after the dispatch pass of the previous frame has been executed.

[0353] S7.3, Command pre-compilation cache and pipeline state reuse.

[0354] S7.3.1 Cache management for distributing Compute Shaders.

[0355] A distribution shader cache pool, VtfCsCachePool, is constructed to cache VkPipeline objects with different stream counts, format combinations, and stride combinations. The XXH3 algorithm is used to hash streamCount, formatConvEnable, and stride to generate a unique cache key. The Least Recently Used (LRU) strategy is adopted. When the number of cached objects reaches the upper limit of 128 by default, the least recently used VkPipeline is evicted to achieve cache eviction.

[0356] S7.3.2 Optimization of pipeline status reuse.

[0357] The original pipeline state object DxvkPipelineState of DXVK is reused, and only the state related to Compute Shader, such as VkPipelineComputeStateCreateInfo, is newly dispatched. To avoid repeatedly creating VkDescriptorSetLayout and VkDescriptorPool, an on-demand creation and reuse strategy is adopted to reduce memory consumption.

[0358] S7.4, Linux kernel scheduling optimization.

[0359] Command priority enhancement: Modify the DRM scheduling module to allocate a high-priority scheduling queue DRM_SCHED_PRIORITY_HIGH to the secondary command buffer of KE-VTFE; Set the scheduling weight to twice that of ordinary commands to ensure that dispatched Passes are executed first and avoid blocking subsequent rendering Passes.

[0360] Affinity scheduling: By using sched_setaffinity, the distribution thread of KE-VTFE is bound to the CPU core associated with the GPU, such as binding AMD GPUs to CPU cores 0-3 and NVIDIA GPUs to CPU cores 4-7, reducing the overhead of CPU core switching.

[0361] Pre-scheduling mechanism: After receiving the KE-VTFE command, the kernel DRM scheduler pre-allocates GPU execution units to avoid idle execution units; if the GPU execution unit is busy, the KE-VTFE command is put into the pre-scheduling queue, and scheduling is carried out immediately once the execution unit is idle.

[0362] S8 and D3D query result write-back and error code mapping. To ensure a complete closed loop in the conversion from D3D API to Vulkan interface, this section supplements the accurate write-back of D3D query results and the one-to-one mapping of error codes.

[0363] S8.1, Refined implementation of writing back the query results of D3D.

[0364] Query results are pre-calculated. When D3D calls EndQuery, KE-VTFE executes. If the result is D3D11_QUERY_SO_STATISTICS, the number of vertices and primitives are obtained through vtfGetTransformFeedbackStatistics, and the D3D11_QUERY_DATA_SO_STATISTICS structure is populated. If the result is D3D11_QUERY_SO_OVERFLOW_PREDICATION, the overflow_flag of vtf_stream_control_block is read, and the D3D11_QUERY_DATA_SO_OVERFLOW_PREDICATION structure PredicateData=overflow_flag is populated.

[0365] Immediate and delayed query processing. Immediate query bImmediate=true: The query result is calculated immediately, written back to the pData buffer, and S_OK is returned. Delayed query bImmediate=false: The query result is stored in the delayed query result queue. When D3D calls GetData and GetDataFlags=D3D11_ASYNC_GETDATA_FLUSH, the queue is refreshed and the result is written back.

[0366] Result consistency guarantee: Before writing back, the visibility of the result data is ensured through a memory barrier. If the query result is not ready, such as the distribution of the Pass is not completed, S_FALSE is returned to inform the D3D application to wait.

[0367] Precise mapping of error codes in S8.2, D3D, and Vulkan:

[0368] Error code mapping table construction: A three-dimensional mapping table is predefined between KE-VTFE error codes, Vulkan error codes, and D3D error codes, covering all possible error scenarios, as shown in the table below:

[0369] Table 1. Three-dimensional mapping table of error codes

[0370] KE-VTFE error scenarios Vulkan error codes D3D Error Code Memory allocation failed VK_ERROR_OUT_OF_DEVICE_MEMORY D3DERR_OUTOFVIDEOMEMORY Format incompatibility VK_ERROR_FORMAT_NOT_SUPPORTED D3DERR_INVALIDCALL illegal state transition VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS D3DERR_INVALIDCALL Buffer overflow VK_ERROR_OUT_OF_BOUNDS D3DERR_DEVICEHUNG Command submission failed VK_ERROR_DEVICE_LOST D3DERR_DEVICELOST

[0371] Error handling process: After each interface call, KE-VTFE checks the returned Vulkan error code; it looks up the corresponding D3D error code through the mapping table, calls the DXVK error feedback module, and returns the D3D error code to the D3D application.

[0372] Error logging: Records error scenarios, error codes, and call stack information to the DXVK log file dxvk.log for easy troubleshooting.

[0373] S9, Full-Scenario Compatibility Adaptation and Dynamic Degradation Mechanism. To cover the feature limitations and driver bugs of different GPUs, a multi-level compatibility adaptation framework is built to achieve full-scenario driver-dependent adaptation.

[0374] S9.1, GPU feature deep detection and dynamic adaptation.

[0375] Feature detection dimensions: The following key features are obtained through vkGetPhysicalDeviceProperties2jivkGetPhysicalDeviceFeatures2: Vulkan version (apiVersion), supported SPIR-V version (spirvVersion), maximum number of storage buffer bindings (maxStorageBuffersPerShaderStage), maximum Compute Shader workgroup size (maxComputeWorkGroupCount), supported texture formats (formats array), and storage buffer write support (shaderStorageBufferWriteAccess).

[0376] Adaptation strategy:

[0377] Advanced adaptation supports SPIR-V 1.3 and above and maxStorageBuffersPerShaderStage≥4, enabling direct writing to the storage buffer and two-stage distribution;

[0378] Intermediate adaptation, supporting SPIR-V 1.1 and above and maxStorageBuffersPerShaderStage≥2, enables storage buffers and single-stream serial distribution, while multi-stream distribution is sequential to avoid insufficient binding count;

[0379] For basic adaptation, if SPIR-V 1.0 is supported or storage buffer writing is not supported, the approach is downgraded to rendering to texture and texture-to-buffer conversion, including: shader rewriting, writing the output data of VS and GS to Vulkan texture VkImage bound to VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; texture format adaptation, selecting a texture format compatible with D3D, such as D3DFMT_R32G32B32_FLOAT corresponding to VK_FORMAT_R32G32B32_SFLOAT; texture-to-buffer conversion, converting texture data to storage buffer through vkCmdCopyImageToBuffer, reusing the kernel shared memory pool and stream partition mapping; kernel-level optimization, implementing direct access to texture memory through drm_prime_handle_to_fd, avoiding CPU involvement in copying, and improving conversion efficiency.

[0380] S9.2, game-scenario adaptation.

[0381] The scene feature library is constructed, and the scene parameters used in the Stream Output of mainstream games are pre-stored, as shown in the table below:

[0382] Table 2. Parameters for Stream Output Application Scenarios

[0383] Game Name Scene Name Stream Quantity Data format call frequency Priority Batch processing threshold Game 1 Hairworks 2 R32G32B32_FLOAT High (60 times / frame) high 8KB Game 2 Particle systems 4 R32G32_FLOAT+R8G8B8A8_UNORM Medium (30 times / frame) middle 16KB Game 3 Character animation 1 R32G32B32A32_FLOAT Low (10 times / frame) Low 32KB

[0384] Scene matching mechanism: When DXVK starts the game, it obtains the path of the game's executable file and extracts the process name through readlink(" / proc / self / exe"); it traverses the scene feature library and fuzzily matches the corresponding scene parameters by process name; it automatically adjusts KE-VTFE parameters, including batch processing threshold, distribution priority, and temporary delivery buffer size.

[0385] S9.3, Driver Bug Avoidance and Blacklist Mechanism.

[0386] Driver blacklist construction: Maintain the vtf_driver_blacklist.conf configuration file to record driver versions with known bugs and their avoidance strategies, as shown in the table below:

[0387] Table 3. Driver Bugs and Avoidance Strategies

[0388] Manufacturers Driver version Known Bug Avoidance strategy NVIDIA 470.xx Data written to the storage buffer by the Compute Shader was not refreshed. Add extra memory barriers AMD RADV 20.3.x Data misalignment during multi-stream distribution Single-stream serial distribution Intel ANV 19.2.x Texture to buffer copy failed Enable CPU-assisted copy

[0389] Driver detection and avoidance: Obtain driver information via vkGetInstanceProcAddr and vkGetDriverProperties. If the driver version is in the blacklist, enable the corresponding avoidance strategy.

[0390] S9.4, Edge Scene Adaptation.

[0391] Empty output scenario (no vertex data written): When vertex_count=0 is detected, skip dispatching Pass and return an empty result directly; avoid creating invalid secondary command buffers and reduce overhead.

[0392] In a circular buffer scenario (using D3D Stream Buffer in a circular manner): monitor currentWriteOffset, and automatically reset it to 0 when the end of the buffer is reached to achieve circular writing; disable the expansion of this partition at the kernel level to avoid breaking the circular logic.

[0393] CPU reads the scene (D3D11_USAGE_STAGING buffer): When the buffer type is D3D11_USAGE_STAGING, after the distribution is completed, vkCmdCopyBufferToBuffer is triggered to copy the device's local memory to the host's visible memory; the memory is mapped to user space through vkMapMemory to ensure that D3D applications can read data through the Map interface.

[0394] This embodiment constructs a KE-VTFE that completely replicates the functional interfaces, state machine, behavioral logic, and error handling of VK_EXT_transform_feedback. It implements core functions of D3D Stream Output, such as multi-stream parallel output, pause and resume, precise counting, and overflow handling, achieving 100% functional equivalence. It supports all D3D 10 / 11 applications that rely on Stream Output, without rendering errors, animation glitches, or missing particles. Through kernel-enhanced shared memory pools, batch processing distribution, deep command queue integration, and kernel scheduling optimization, the performance loss of the simulation solution is controlled within 10%, far lower than the 30% to 100% of existing solutions. When running games on older GPUs such as NVIDIA Kepler, the frame rate can reach over 30 FPS, meeting the requirements of real-time games. It completes the D3D query result write-back and error code mapping stages, realizing a complete closed loop of D3D API interception, parameter parsing, function mapping, data capture, state synchronization, command submission, result write-back, and error feedback, ensuring that D3D applications can correctly obtain state and error information. It supports all Vulkan applications. GPUs with drivers version 1.0 and above include AMD GCN 1.0, NVIDIA Kepler, Intel Gen7 and 8, etc. Through dynamic downgrade mechanisms, scenario-based adaptation, and driver bug avoidance, it covers more than 99% of game scenarios that rely on Stream Output. The entire process does not modify the GPU chip driver source code, and compatibility is achieved only through DXVK user-space extensions and Linux kernel customization. KE-VTFE is decoupled from the upper-layer logic of DXVK. The Linux kernel modifications are concentrated on memory, scheduling, and DRM modules, without affecting the original kernel functions. The scene feature library and driver blacklist support dynamic updates without the need to refactor the core code.

[0395] 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 method for virtual implementation of Direct3D stream output based on kernel enhancement, characterized in that, Specifically, the following steps are included: During the DXVK compilation phase, the shader code is parsed, and VS stream data and GS stream data are constructed to describe the stream output attributes of the vertex shader and geometry shader, respectively. A first mapping table is established between the buffer formats of D3D, DXVK, and kernels, and conversion instruction fragments generated for incompatible formats are written into the first mapping table. The scene parameters are recorded using the scene library for D3D application stream output. When the system kernel starts, it allocates the kernel-enhanced shared memory pool, divides it into free partitions of different sizes, and establishes a second mapping table for streams to partitions; after DXVK starts, it establishes a first abstraction layer with data structures and interfaces consistent with the VK_EXT_transform_feedback extension, maps the errors of the first abstraction layer to the extended standard error codes, and then converts them to D3D error codes; When a D3D application starts, DXVK sets up the first abstraction layer based on scene parameters obtained from the scene library. When calling the buffer binding class API, it parses the buffer parameters, passes the parameters to the kernel to allocate a shared memory pool partition for the stream, updates the second mapping table, and saves the conversion instruction fragment obtained from the first mapping table to the second mapping table if the formats are incompatible. When calling the flow control class API, it establishes the binding relationship between the query object and the target stream, and triggers shader rewriting if a corresponding shader exists. When calling the query operation API, extract the query parameters, convert the D3D buffer and topology type, and mark the dynamic buffer; The vertex shader and geometry shader respectively write the generated VS stream data and GS stream data into the corresponding temporary delivery buffers. The first abstraction layer fills in the unified buffer object UBO according to the second mapping table, binds the computation shader to the UBO, the temporary delivery buffer and the stream partition buffer, and the computation shader completes the distribution. The distribution path of the first abstraction layer is encapsulated as a Vulkan secondary command buffer. The DXVK rendering thread command pool is reused, and the secondary command buffer is submitted in batches to the DXVK main command queue. The main command queue then submits the commands to the GPU for execution to complete the rendering.

2. The Direct3D stream output virtual implementation method according to claim 1, characterized in that, VS stream data is used to describe the stream output attributes of the vertex shader, including stream index, number of attributes, format checksum, vertex ID, and flexible array, where the flexible array is used to record attribute offsets and formats.

3. The Direct3D stream output virtual implementation method according to claim 1, characterized in that, GS stream data includes the stream index of the vertex, the primitive to which it belongs, the vertex index within the current primitive, and vertex attributes, where vertex attributes include normals, colors, and texture coordinates.

4. The Direct3D stream output virtual implementation method according to claim 1, characterized in that, The free partitions are managed by establishing a free partition linked list, which is established using a doubly linked circular list structure.

5. The Direct3D stream output virtual implementation method according to claim 1, characterized in that, The first abstraction layer fills the unified buffer object UBO according to the second mapping table in the following way: when the usage rate of the temporary delivery buffer is greater than the threshold or the stream output is terminated, the first abstraction layer fills all the dynamic parameters required for the distribution stage into the unified buffer object UBO according to the second mapping table. The dynamic parameters include the number of streams, format conversion instructions and partition addresses. Memory barriers are set to ensure that the compute shader is read after the shader is written.

6. The Direct3D stream output virtual implementation method according to claim 1, characterized in that, When the system kernel starts, it also constructs a stream state control block, which includes state flags, write offsets, number of output vertices, number of output primitives, overflow flags, futex wait queues, associated stream partitions and synchronization locks, and initializes its state flags to idle.

7. The Direct3D stream output virtual implementation method according to claim 6, characterized in that, When the D3D application pauses the stream output, the first abstraction layer obtains the current stream state control block. If the state flag is active, the state flag is changed to paused, the stream state control block is locked, and the current values ​​of the write offset, the number of output vertices, and the number of output primitives are read from the stream state control block using atomic read operations. The current values ​​are stored in the first local variable, the DXVK rendering thread is suspended and added to the kernel futex wait queue. If the status is inactive, an error message will be returned.

8. The Direct3D stream output virtual implementation method according to claim 6, characterized in that, When the D3D application resumes streaming output, the first abstraction layer obtains the current stream state control block. If the state is marked as paused, the state is changed to active, the stream state control block is locked, the rendering thread in the futex waiting queue is woken up, the stream state control block is unlocked, and a success is returned. If the status is marked as not paused, an error message is returned.

9. The Direct3D stream output virtual implementation method according to claim 6, characterized in that, When a D3D application retrieves stream output statistics or query results, the first abstraction layer obtains the number of vertices and primitives through atomic reads, then maps the obtained kernel-state raw counts to a D3D structure, writes the structure to a buffer specified by the D3D application, and enables the D3D application to retrieve the query results.

10. The Direct3D stream output virtual implementation method according to claim 4, characterized in that, When a user-mode memory allocation request is received, a spinlock is used to lock the shared memory pool. The free capacity in the shared memory pool is checked to see if it meets the requested memory size. If it does not, a reserved memory block is enabled and an error message is returned if the reserved memory is still insufficient. Otherwise, a partition of the same size is searched in the free partition list. If no partition exists, the large partition is split. The partition's data format description and buffer usage flag are initialized, the lifecycle state is set to allocated but not bound or to be used, and the reference count is set to 1. Unlock the shared memory pool and return the partition ID and user-mode mapping address.