Files
d4r/patches/vkd3d-proton/0001-split-command-list-for-external-wait.patch
T
ayo 7c5d3c9157 feat: add d4r inference engines and Windows parity tooling
Add native K/M/RR inference engines, HIP kernels, replay and validation
harnesses, and Windows DLSS setup and runtime support. Include GPU
profiling tools, build and packaging scripts, platform integration
patches, and implementation documentation.
2026-10-16 08:54:47 -04:00

305 lines
14 KiB
Diff

diff --git a/include/vkd3d_device_vkd3d_ext.idl b/include/vkd3d_device_vkd3d_ext.idl
index 680f2c96..1fddc28e 100644
--- a/include/vkd3d_device_vkd3d_ext.idl
+++ b/include/vkd3d_device_vkd3d_ext.idl
@@ -104,6 +104,21 @@ interface ID3D12DXVKInteropDevice3 : ID3D12DXVKInteropDevice2
UINT32 *vk_memory_type);
}
+[
+ uuid(5a7c8b3e-2f61-4d0e-9c1a-7e3b52d4a901),
+ object,
+ local,
+ pointer_default(unique)
+]
+interface ID3D12DXVKInteropDeviceD4R : ID3D12DXVKInteropDevice3
+{
+ /* d4r: ends the command list's current Vulkan command buffer. Everything
+ * recorded afterwards is submitted separately and waits (as a queue
+ * submission wait, not a GPU-side wait) until the timeline semaphore
+ * vk_semaphore reaches value. */
+ HRESULT SplitCommandListForExternalWait(ID3D12CommandList *list, UINT64 vk_semaphore, UINT64 value);
+}
+
[
uuid(f3112584-41f9-348d-a59b-00b7e1d285d6),
object,
diff --git a/libs/vkd3d/command.c b/libs/vkd3d/command.c
index 611e373b..23af78a5 100644
--- a/libs/vkd3d/command.c
+++ b/libs/vkd3d/command.c
@@ -2557,6 +2557,38 @@ static void d3d12_command_list_consider_new_sequence(struct d3d12_command_list *
}
}
+HRESULT d3d12_command_list_split_for_external_wait(struct d3d12_command_list *list,
+ VkSemaphore semaphore, uint64_t value)
+{
+ struct d3d12_command_list_iteration *iteration;
+ unsigned int count;
+
+ if (!list->is_recording)
+ return E_INVALIDARG;
+ if (!d3d12_command_list_allows_new_sequence(list))
+ {
+ WARN("Cannot split command list for external wait with a running query.\n");
+ return E_FAIL;
+ }
+
+ /* Flush everything recorded so far into the current command buffer. */
+ d3d12_command_list_decay_tracked_state(list);
+ d3d12_command_list_invalidate_all_state(list);
+
+ count = list->cmd.iteration_count;
+ d3d12_command_list_begin_new_sequence(list, false);
+ if (list->cmd.iteration_count == count)
+ {
+ WARN("Out of command list sequences for external wait.\n");
+ return E_FAIL;
+ }
+
+ iteration = &list->cmd.iterations[list->cmd.iteration_count - 1];
+ iteration->external_wait_semaphore = semaphore;
+ iteration->external_wait_value = value;
+ return S_OK;
+}
+
static void d3d12_command_list_copy_queue_fallback(struct d3d12_command_list *list)
{
if (!list->cmd.iterations[list->cmd.iteration_count - 1].fallback)
@@ -22769,6 +22801,7 @@ static void STDMETHODCALLTYPE d3d12_command_queue_ExecuteCommandLists(ID3D12Comm
struct vkd3d_initial_transition *transitions;
size_t num_transitions, num_command_buffers;
VkCommandBufferSubmitInfo *buffers, *buffer;
+ VkSemaphoreSubmitInfo *external_waits = NULL;
struct d3d12_command_allocator **allocators;
struct d3d12_command_queue_submission sub;
unsigned int indirect_barrier_hoist_index;
@@ -22954,6 +22987,7 @@ static void STDMETHODCALLTYPE d3d12_command_queue_ExecuteCommandLists(ID3D12Comm
vkd3d_free(allocators);
vkd3d_free(buffers);
vkd3d_free(cmd_cost);
+ vkd3d_free(external_waits);
#ifdef VKD3D_ENABLE_BREADCRUMBS
vkd3d_free(breadcrumb_indices);
#endif
@@ -23036,6 +23070,21 @@ static void STDMETHODCALLTYPE d3d12_command_queue_ExecuteCommandLists(ID3D12Comm
buffer->commandBuffer = cmd_list->cmd.iterations[iter].vk_command_buffer;
/* Pilfer this for purposes of signalling aux data. No need to allocate more stuff just to signal this. */
buffer->deviceMask = cmd_list->cmd.iterations[iter].fallback ? VKD3D_COMMAND_BUFFER_SUBMIT_INFO_DEVICE_MASK_FALLBACK_QUEUE : 0;
+
+ if (cmd_list->cmd.iterations[iter].external_wait_semaphore)
+ {
+ if (!external_waits)
+ external_waits = vkd3d_calloc(num_command_buffers, sizeof(*external_waits));
+ if (external_waits)
+ {
+ VkSemaphoreSubmitInfo *wait = &external_waits[cmd_submit_count - 1];
+ wait->sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO;
+ wait->semaphore = cmd_list->cmd.iterations[iter].external_wait_semaphore;
+ wait->value = cmd_list->cmd.iterations[iter].external_wait_value;
+ wait->stageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT;
+ buffer->deviceMask |= VKD3D_COMMAND_BUFFER_SUBMIT_INFO_DEVICE_MASK_EXTERNAL_WAIT;
+ }
+ }
}
need_suspend_fixup = cmd_list->cmd.suspend_resume.suspend.vk_fixup_cmd_buffer && (i + 1 == command_list_count ||
@@ -23155,6 +23204,7 @@ static void STDMETHODCALLTYPE d3d12_command_queue_ExecuteCommandLists(ID3D12Comm
sub.type = VKD3D_SUBMISSION_EXECUTE;
sub.execute.cmd = buffers;
sub.execute.cmd_cost = cmd_cost;
+ sub.execute.external_waits = external_waits;
sub.execute.cmd_count = cmd_submit_count;
sub.execute.command_allocators = allocators;
sub.execute.num_command_allocators = command_list_count;
@@ -24377,6 +24427,8 @@ static void d3d12_command_queue_execute(struct d3d12_command_queue *command_queu
VkSemaphoreSubmitInfo *binary_semaphore_info;
bool stagger_submissions, is_first, is_last;
VkSemaphoreSubmitInfo wait_semaphore_info;
+ VkSemaphoreSubmitInfo combined_wait_infos[2];
+ bool external_wait;
uint32_t cmd_index, cmd_count, total_cost;
struct vkd3d_fence_wait_info fence_info;
VkSubmitInfo2 submit_desc[2], *submit;
@@ -24516,7 +24568,15 @@ static void d3d12_command_queue_execute(struct d3d12_command_queue *command_queu
total_cost = 0;
for (i = cmd_index + cmd_count; i < exec->cmd_count && total_cost < VKD3D_COMMAND_COST_MERGE_THRESHOLD; i++)
+ {
+ /* d4r: never merge across an external wait. */
+ if (exec->cmd[i].deviceMask & VKD3D_COMMAND_BUFFER_SUBMIT_INFO_DEVICE_MASK_EXTERNAL_WAIT)
+ {
+ total_cost = VKD3D_COMMAND_COST_MERGE_THRESHOLD;
+ break;
+ }
total_cost += exec->cmd_cost[i];
+ }
if (total_cost < VKD3D_COMMAND_COST_MERGE_THRESHOLD)
cmd_count = exec->cmd_count - cmd_index;
@@ -24525,8 +24585,14 @@ static void d3d12_command_queue_execute(struct d3d12_command_queue *command_queu
{
/* Submit everything at once, until we hit a fallback queue submission. Pick that up next iteration. */
for (cmd_count = 0; cmd_index + cmd_count < exec->cmd_count; cmd_count++)
+ {
if (exec->cmd[cmd_index + cmd_count].deviceMask & VKD3D_COMMAND_BUFFER_SUBMIT_INFO_DEVICE_MASK_FALLBACK_QUEUE)
break;
+ /* d4r: an external wait starts a new submit. */
+ if (cmd_count && (exec->cmd[cmd_index + cmd_count].deviceMask &
+ VKD3D_COMMAND_BUFFER_SUBMIT_INFO_DEVICE_MASK_EXTERNAL_WAIT))
+ break;
+ }
}
submit = &submit_desc[num_submits++];
@@ -24536,6 +24602,9 @@ static void d3d12_command_queue_execute(struct d3d12_command_queue *command_queu
submit->signalSemaphoreInfoCount = 1;
submit->pSignalSemaphoreInfos = signal_semaphore_infos;
+ external_wait = cmd_count && exec->external_waits &&
+ (exec->cmd[cmd_index].deviceMask & VKD3D_COMMAND_BUFFER_SUBMIT_INFO_DEVICE_MASK_EXTERNAL_WAIT);
+
/* Clear out any shenanigans we added due to fallback submit. */
for (i = 0; i < cmd_count; i++)
exec->cmd[cmd_index + i].deviceMask = 0;
@@ -24555,6 +24624,23 @@ static void d3d12_command_queue_execute(struct d3d12_command_queue *command_queu
submit->pWaitSemaphoreInfos = &wait_semaphore_info;
}
+ if (external_wait)
+ {
+ /* d4r: a queue-level wait-before-signal; the host signals the value later. */
+ if (submit->waitSemaphoreInfoCount)
+ {
+ combined_wait_infos[0] = submit->pWaitSemaphoreInfos[0];
+ combined_wait_infos[1] = exec->external_waits[cmd_index];
+ submit->waitSemaphoreInfoCount = 2;
+ submit->pWaitSemaphoreInfos = combined_wait_infos;
+ }
+ else
+ {
+ submit->waitSemaphoreInfoCount = 1;
+ submit->pWaitSemaphoreInfos = &exec->external_waits[cmd_index];
+ }
+ }
+
cmd_index += cmd_count;
is_last = cmd_index == exec->cmd_count;
@@ -25347,6 +25433,7 @@ static void *d3d12_command_queue_submission_worker_main(void *userdata)
* On error, the counters are freed early, so there is no risk of leak. */
vkd3d_free(submission.execute.cmd);
vkd3d_free(submission.execute.cmd_cost);
+ vkd3d_free(submission.execute.external_waits);
vkd3d_free(submission.execute.transitions);
#ifdef VKD3D_ENABLE_BREADCRUMBS
for (i = 0; i < submission.execute.breadcrumb_indices_count; i++)
diff --git a/libs/vkd3d/device.c b/libs/vkd3d/device.c
index f6db6954..06955941 100644
--- a/libs/vkd3d/device.c
+++ b/libs/vkd3d/device.c
@@ -4657,7 +4657,8 @@ HRESULT STDMETHODCALLTYPE d3d12_device_QueryInterface(d3d12_device_iface *iface,
if (IsEqualGUID(riid, &IID_ID3D12DXVKInteropDevice)
|| IsEqualGUID(riid, &IID_ID3D12DXVKInteropDevice1)
|| IsEqualGUID(riid, &IID_ID3D12DXVKInteropDevice2)
- || IsEqualGUID(riid, &IID_ID3D12DXVKInteropDevice3))
+ || IsEqualGUID(riid, &IID_ID3D12DXVKInteropDevice3)
+ || IsEqualGUID(riid, &IID_ID3D12DXVKInteropDeviceD4R))
{
d3d12_dxvk_interop_device_AddRef(&device->ID3D12DXVKInteropDevice_iface);
*object = &device->ID3D12DXVKInteropDevice_iface;
@@ -11379,7 +11380,7 @@ static void d3d12_device_replace_vtable(struct d3d12_device *device)
}
extern CONST_VTBL struct ID3D12DeviceExt5Vtbl d3d12_device_vkd3d_ext_vtbl;
-extern CONST_VTBL struct ID3D12DXVKInteropDevice3Vtbl d3d12_dxvk_interop_device_vtbl;
+extern CONST_VTBL struct ID3D12DXVKInteropDeviceD4RVtbl d3d12_dxvk_interop_device_vtbl;
extern CONST_VTBL struct ID3DLowLatencyDeviceVtbl d3d_low_latency_device_vtbl;
extern CONST_VTBL struct IAmdExtAntiLagApiVtbl d3d_amd_ext_anti_lag_vtbl;
diff --git a/libs/vkd3d/device_vkd3d_ext.c b/libs/vkd3d/device_vkd3d_ext.c
index 94818b9a..bd5dd7a5 100644
--- a/libs/vkd3d/device_vkd3d_ext.c
+++ b/libs/vkd3d/device_vkd3d_ext.c
@@ -1145,7 +1145,19 @@ static HRESULT STDMETHODCALLTYPE d3d12_dxvk_interop_device_GetVulkanHeapInfo(d3d
return S_OK;
}
-CONST_VTBL struct ID3D12DXVKInteropDevice3Vtbl d3d12_dxvk_interop_device_vtbl =
+static HRESULT STDMETHODCALLTYPE d3d12_dxvk_interop_device_SplitCommandListForExternalWait(
+ d3d12_dxvk_interop_device_iface *iface, ID3D12CommandList *list, UINT64 vk_semaphore, UINT64 value)
+{
+ struct d3d12_command_list *cmd_list = d3d12_command_list_from_iface(list);
+
+ TRACE("iface %p, list %p, vk_semaphore 0x%"PRIx64", value %"PRIu64".\n", iface, list, vk_semaphore, value);
+
+ if (!cmd_list || !vk_semaphore)
+ return E_INVALIDARG;
+ return d3d12_command_list_split_for_external_wait(cmd_list, (VkSemaphore)vk_semaphore, value);
+}
+
+CONST_VTBL struct ID3D12DXVKInteropDeviceD4RVtbl d3d12_dxvk_interop_device_vtbl =
{
/* IUnknown methods */
d3d12_dxvk_interop_device_QueryInterface,
@@ -1177,6 +1189,9 @@ CONST_VTBL struct ID3D12DXVKInteropDevice3Vtbl d3d12_dxvk_interop_device_vtbl =
/* ID3D12DXVKInteropDevice3 methods */
d3d12_dxvk_interop_device_GetVulkanHeapInfo,
+
+ /* ID3D12DXVKInteropDeviceD4R methods */
+ d3d12_dxvk_interop_device_SplitCommandListForExternalWait,
};
static inline struct d3d12_device *d3d12_device_from_ID3DLowLatencyDevice(d3d_low_latency_device_iface *iface)
diff --git a/libs/vkd3d/vkd3d_private.h b/libs/vkd3d/vkd3d_private.h
index a2a78000..64338585 100644
--- a/libs/vkd3d/vkd3d_private.h
+++ b/libs/vkd3d/vkd3d_private.h
@@ -3207,6 +3207,9 @@ struct d3d12_command_list_iteration
uint32_t estimated_cost;
bool fallback; /* Only applies to vk_command_buffer. */
struct d3d12_command_list_iteration_indirect_meta indirect_meta;
+ /* d4r: vk_command_buffer is submitted separately, after this timeline value is reached. */
+ VkSemaphore external_wait_semaphore;
+ uint64_t external_wait_value;
};
enum
@@ -3216,9 +3219,12 @@ enum
* without having to add more allocations. */
VKD3D_COMMAND_BUFFER_SUBMIT_INFO_DEVICE_MASK_DEFAULT = 0,
VKD3D_COMMAND_BUFFER_SUBMIT_INFO_DEVICE_MASK_FALLBACK_QUEUE = 1u << 0,
+ /* d4r: starts a new submit that waits for submission_execute::external_waits[index]. */
+ VKD3D_COMMAND_BUFFER_SUBMIT_INFO_DEVICE_MASK_EXTERNAL_WAIT = 1u << 1,
};
-#define VKD3D_MAX_COMMAND_LIST_SEQUENCES 2
+/* d4r: 4 so an external-wait split fits next to DGC and fallback splits. */
+#define VKD3D_MAX_COMMAND_LIST_SEQUENCES 4
#define VKD3D_COMMAND_COST_LOW (1u)
#define VKD3D_COMMAND_COST_HIGH (16u)
@@ -3737,6 +3743,8 @@ struct d3d12_command_queue_submission_execute
{
VkCommandBufferSubmitInfo *cmd;
uint32_t *cmd_cost;
+ /* d4r: indexed like cmd, only allocated when a command buffer waits externally. */
+ VkSemaphoreSubmitInfo *external_waits;
struct d3d12_command_allocator **command_allocators;
UINT cmd_count;
UINT num_command_allocators;
@@ -5463,7 +5471,10 @@ struct vkd3d_timestamp_profiler;
typedef ID3D12DeviceExt5 d3d12_device_vkd3d_ext_iface;
/* ID3D12DXVKInteropDevice */
-typedef ID3D12DXVKInteropDevice3 d3d12_dxvk_interop_device_iface;
+HRESULT d3d12_command_list_split_for_external_wait(struct d3d12_command_list *list,
+ VkSemaphore semaphore, uint64_t value);
+
+typedef ID3D12DXVKInteropDeviceD4R d3d12_dxvk_interop_device_iface;
/* ID3DLowLatencyDevice */
typedef ID3DLowLatencyDevice d3d_low_latency_device_iface;