vkd3d: Avoid vkResetCommandPool if there are pending command buffers.

Works around an app-bug in SotTR, where the command pool is reset before
the command buffer completes.

Signed-off-by: Hans-Kristian Arntzen <post@arntzen-software.no>
This commit is contained in:
Hans-Kristian Arntzen 2020-04-17 15:10:07 +02:00
parent 334b1cab8d
commit 09af24e69b
2 changed files with 42 additions and 1 deletions

View File

@ -1038,6 +1038,7 @@ static HRESULT d3d12_command_allocator_allocate_command_buffer(struct d3d12_comm
}
allocator->current_command_list = list;
list->outstanding_submissions_count = &allocator->outstanding_submissions_count;
return S_OK;
}
@ -1500,6 +1501,7 @@ static HRESULT STDMETHODCALLTYPE d3d12_command_allocator_Reset(ID3D12CommandAllo
const struct vkd3d_vk_device_procs *vk_procs;
struct d3d12_command_list *list;
struct d3d12_device *device;
LONG pending;
VkResult vr;
TRACE("iface %p.\n", iface);
@ -1515,6 +1517,23 @@ static HRESULT STDMETHODCALLTYPE d3d12_command_allocator_Reset(ID3D12CommandAllo
TRACE("Resetting command list %p.\n", list);
}
if ((pending = atomic_add_fetch(&allocator->outstanding_submissions_count, 0)) != 0)
{
/* HACK: There are currently command lists waiting to be submitted to the queue in the submission threads.
* Buggy application, but work around this by not resetting the command pool this time.
* To be perfectly safe, we can only reset after the fence timeline is signalled,
* however, this is enough to workaround SotTR which resets the command list right
* after calling ID3D12CommandQueue::ExecuteCommandLists().
* Only happens once or twice on bootup and doesn't cause memory leaks over time
* since the command pool is eventually reset.
* Game does not seem to care if E_FAIL is returned, which is the correct thing to do here.
*
* TODO: Guard this with actual timeline semaphores from vkQueueSubmit(). */
ERR("There are still %u pending command lists awaiting execution from command allocator iface %p!\n",
(unsigned int)pending, iface);
return E_FAIL;
}
device = allocator->device;
vk_procs = &device->vk_procs;
@ -1595,7 +1614,7 @@ static HRESULT d3d12_command_allocator_init(struct d3d12_command_allocator *allo
allocator->ID3D12CommandAllocator_iface.lpVtbl = &d3d12_command_allocator_vtbl;
allocator->refcount = 1;
allocator->outstanding_submissions_count = 0;
allocator->type = type;
allocator->vk_queue_flags = queue->vk_queue_flags;
@ -6081,6 +6100,7 @@ static void STDMETHODCALLTYPE d3d12_command_queue_ExecuteCommandLists(ID3D12Comm
struct d3d12_command_queue_submission sub;
struct d3d12_command_list *cmd_list;
VkCommandBuffer *buffers;
LONG **outstanding;
unsigned int i, j;
TRACE("iface %p, command_list_count %u, command_lists %p.\n",
@ -6092,6 +6112,12 @@ static void STDMETHODCALLTYPE d3d12_command_queue_ExecuteCommandLists(ID3D12Comm
return;
}
if (!(outstanding = vkd3d_calloc(command_list_count, sizeof(*outstanding))))
{
ERR("Failed to allocate outstanding submissions count.\n");
return;
}
for (i = 0; i < command_list_count; ++i)
{
cmd_list = unsafe_impl_from_ID3D12CommandList(command_lists[i]);
@ -6104,6 +6130,9 @@ static void STDMETHODCALLTYPE d3d12_command_queue_ExecuteCommandLists(ID3D12Comm
return;
}
outstanding[i] = cmd_list->outstanding_submissions_count;
InterlockedIncrement(outstanding[i]);
for (j = 0; j < cmd_list->descriptor_updates_count; j++)
d3d12_deferred_descriptor_set_update_resolve(cmd_list, &cmd_list->descriptor_updates[j]);
buffers[i] = cmd_list->vk_command_buffer;
@ -6112,6 +6141,7 @@ static void STDMETHODCALLTYPE d3d12_command_queue_ExecuteCommandLists(ID3D12Comm
sub.type = VKD3D_SUBMISSION_EXECUTE;
sub.u.execute.cmd = buffers;
sub.u.execute.count = command_list_count;
sub.u.execute.outstanding_submissions_count = outstanding;
d3d12_command_queue_add_submission(command_queue, &sub);
}
@ -6470,6 +6500,8 @@ static void *d3d12_command_queue_submission_worker_main(void *userdata)
{
struct d3d12_command_queue_submission submission;
struct d3d12_command_queue *queue = userdata;
unsigned int i;
vkd3d_set_thread_name("vkd3d_queue");
for (;;)
@ -6499,6 +6531,10 @@ static void *d3d12_command_queue_submission_worker_main(void *userdata)
case VKD3D_SUBMISSION_EXECUTE:
d3d12_command_queue_execute(queue, submission.u.execute.cmd, submission.u.execute.count);
vkd3d_free(submission.u.execute.cmd);
/* TODO: The correct place to do this would be in a fence handler, but this is good enough for now. */
for (i = 0; i < submission.u.execute.count; i++)
InterlockedDecrement(submission.u.execute.outstanding_submissions_count[i]);
vkd3d_free(submission.u.execute.outstanding_submissions_count);
break;
case VKD3D_SUBMISSION_DRAIN:

View File

@ -1032,6 +1032,8 @@ struct d3d12_command_allocator
size_t command_buffers_size;
size_t command_buffer_count;
LONG outstanding_submissions_count;
struct d3d12_command_list *current_command_list;
struct d3d12_device *device;
@ -1164,6 +1166,8 @@ struct d3d12_command_list
size_t descriptor_updates_size;
size_t descriptor_updates_count;
LONG *outstanding_submissions_count;
struct vkd3d_private_store private_store;
};
@ -1214,6 +1218,7 @@ struct d3d12_command_queue_submission_signal
struct d3d12_command_queue_submission_execute
{
VkCommandBuffer *cmd;
LONG **outstanding_submissions_count;
UINT count;
};