Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deferred device destroy #2134

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion include/private/vkd3d_threads.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ struct pthread

HMODULE d3d12core_reference;
HMODULE dxgi_reference;
LONG refcount;
};
typedef struct pthread *pthread_t;

Expand Down Expand Up @@ -80,10 +81,16 @@ static DWORD WINAPI win32_thread_wrapper_routine(void *arg)
/* We are executing in d3d12core.dll here, so we have to use the atomic FreeLibraryAndExit thread to make this work. */
if (thread->d3d12core_reference)
{
HMODULE module_ref = thread->d3d12core_reference;
TRACE("Releasing module reference for d3d12core.dll and exiting thread: %p.\n", thread->d3d12core_reference);
FreeLibraryAndExitThread(thread->d3d12core_reference, 0);
if (!InterlockedDecrement(&thread->refcount))
vkd3d_free(thread);
FreeLibraryAndExitThread(module_ref, 0);
}

if (!InterlockedDecrement(&thread->refcount))
vkd3d_free(thread);

/* Otherwise, fall back to the implicit ExitThread(). */
return 0;
}
Expand All @@ -97,6 +104,7 @@ static inline int pthread_create(pthread_t *out_thread, void *attr, void * (*thr
(void)attr;
thread->routine = thread_fun;
thread->arg = arg;
thread->refcount = 2;

/* Need GetModuleHandleEx which lets us get a refcount. */
if (!GetModuleHandleExA(0, "d3d12core.dll", &thread->d3d12core_reference))
Expand Down Expand Up @@ -134,6 +142,14 @@ static inline int pthread_join(pthread_t thread, void **ret)
return success ? 0 : -1;
}

static inline int pthread_detach(pthread_t thread)
{
CloseHandle(thread->thread);
if (!InterlockedDecrement(&thread->refcount))
vkd3d_free(thread);
return 0;
}

static inline int pthread_mutex_init(pthread_mutex_t *lock, void *attr)
{
(void)attr;
Expand Down
1 change: 1 addition & 0 deletions include/vkd3d.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ extern "C" {
#define VKD3D_CONFIG_FLAG_NO_STAGGERED_SUBMIT (1ull << 50)
#define VKD3D_CONFIG_FLAG_CLEAR_UAV_SYNC (1ull << 51)
#define VKD3D_CONFIG_FLAG_FORCE_DYNAMIC_MSAA (1ull << 52)
#define VKD3D_CONFIG_FLAG_DELAY_DEVICE_DESTRUCTION (1ull << 53)

struct vkd3d_instance;

Expand Down
40 changes: 38 additions & 2 deletions libs/vkd3d/device.c
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,10 @@ static const struct vkd3d_instance_application_meta application_override[] = {
{ VKD3D_STRING_COMPARE_STARTS_WITH, "tlou-i", VKD3D_CONFIG_FLAG_NO_STAGGERED_SUBMIT, 0 },
/* Skull and Bones (2853730). Seems to require unsupported dcomp when reflex is enabled for some reason *shrug */
{ VKD3D_STRING_COMPARE_EXACT, "skullandbones.exe", 0, 0, VKD3D_APPLICATION_FEATURE_DISABLE_NV_REFLEX },
/* Test Drive Unlimited Solar Crown (1249970).
* Device releases ID3D12Device too many times, leading to subsequent crash on fence release.
* Appears to hang on Windows though, so ... */
{ VKD3D_STRING_COMPARE_EXACT, "TDUSC.exe", VKD3D_CONFIG_FLAG_DELAY_DEVICE_DESTRUCTION, 0 },
/* Unreal Engine catch-all. ReBAR is a massive uplift on RX 7600 for example in Wukong.
* AMD windows drivers also seem to have some kind of general app-opt for UE titles.
* Use no-staggered-submit by default on UE. We've only observed issues in Wukong here, but
Expand Down Expand Up @@ -3531,6 +3535,20 @@ ULONG d3d12_device_add_ref_common(struct d3d12_device *device)
return InterlockedIncrement(&device->refcount);
}

static void *device_destroy_thread(void *args)
{
struct d3d12_device *device = args;
#ifdef _WIN32
Sleep(1000);
#else
sleep(1);
#endif
INFO("Destroying device in a delayed way.\n");
d3d12_device_destroy(device);
vkd3d_free_aligned(device);
return NULL;
}

ULONG d3d12_device_release_common(struct d3d12_device *device)
{
ULONG cur_refcount, cas_refcount;
Expand Down Expand Up @@ -3559,8 +3577,26 @@ ULONG d3d12_device_release_common(struct d3d12_device *device)
if (cur_refcount == 1)
{
d3d12_remove_device_singleton(device->adapter_luid);
d3d12_device_destroy(device);
vkd3d_free_aligned(device);

if (vkd3d_config_flags & VKD3D_CONFIG_FLAG_DELAY_DEVICE_DESTRUCTION)
{
pthread_t thr;
INFO("Device hit 0 ref-count, but deferring destruction due to config flag.\n");
/* Workaround buggy games that release too many device references before all child objects
* are destroyed. This crashes native Windows too. */
if (pthread_create(&thr, NULL, device_destroy_thread, device))
{
d3d12_device_destroy(device);
vkd3d_free_aligned(device);
}
else
pthread_detach(thr);
}
else
{
d3d12_device_destroy(device);
vkd3d_free_aligned(device);
}
}

if (is_locked)
Expand Down