Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=51665 Signed-off-by: Zhiyi Zhang zzhang@codeweavers.com --- dlls/dxgi/adapter.c | 13 +++- dlls/dxgi/tests/dxgi.c | 4 -- dlls/wined3d/directx.c | 143 ++++++++++++++++++++++++++++++++++++++ dlls/wined3d/wined3d.spec | 2 + include/wine/wined3d.h | 3 + 5 files changed, 158 insertions(+), 7 deletions(-)
diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c index 20c017eba08..b178376341b 100644 --- a/dlls/dxgi/adapter.c +++ b/dlls/dxgi/adapter.c @@ -333,15 +333,22 @@ static HRESULT STDMETHODCALLTYPE dxgi_adapter_SetVideoMemoryReservation(IWineDXG static HRESULT STDMETHODCALLTYPE dxgi_adapter_RegisterVideoMemoryBudgetChangeNotificationEvent( IWineDXGIAdapter *iface, HANDLE event, DWORD *cookie) { - FIXME("iface %p, event %p, cookie %p stub!\n", iface, event, cookie); + struct dxgi_adapter *adapter = impl_from_IWineDXGIAdapter(iface);
- return E_NOTIMPL; + TRACE("iface %p, event %p, cookie %p.\n", iface, event, cookie); + + if (!event || !cookie) + return DXGI_ERROR_INVALID_CALL; + + return wined3d_adapter_register_budget_change_notification(adapter->wined3d_adapter, event, cookie); }
static void STDMETHODCALLTYPE dxgi_adapter_UnregisterVideoMemoryBudgetChangeNotification( IWineDXGIAdapter *iface, DWORD cookie) { - FIXME("iface %p, cookie %#lx stub!\n", iface, cookie); + TRACE("iface %p, cookie %#lx.\n", iface, cookie); + + wined3d_adapter_unregister_budget_change_notification(cookie); }
static HRESULT STDMETHODCALLTYPE dxgi_adapter_GetDesc3(IWineDXGIAdapter *iface, DXGI_ADAPTER_DESC3 *desc) diff --git a/dlls/dxgi/tests/dxgi.c b/dlls/dxgi/tests/dxgi.c index 04bba29e8e5..33e9ead31ad 100644 --- a/dlls/dxgi/tests/dxgi.c +++ b/dlls/dxgi/tests/dxgi.c @@ -7539,16 +7539,13 @@ static void test_video_memory_budget_notification(void) goto done;
hr = IDXGIAdapter3_RegisterVideoMemoryBudgetChangeNotificationEvent(adapter3, NULL, &cookie); - todo_wine ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#lx.\n", hr);
event = CreateEventW(NULL, FALSE, FALSE, NULL); hr = IDXGIAdapter3_RegisterVideoMemoryBudgetChangeNotificationEvent(adapter3, event, NULL); - todo_wine ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#lx.\n", hr);
hr = IDXGIAdapter3_RegisterVideoMemoryBudgetChangeNotificationEvent(adapter3, event, &cookie); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); hr = IDXGIAdapter3_QueryVideoMemoryInfo(adapter3, 0, DXGI_MEMORY_SEGMENT_GROUP_LOCAL, &memory_info); ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); @@ -7560,7 +7557,6 @@ static void test_video_memory_budget_notification(void) if (memory_info.Budget) { ret = WaitForSingleObject(event, 1000); - todo_wine ok(ret == WAIT_OBJECT_0, "Expected event fired.\n"); }
diff --git a/dlls/wined3d/directx.c b/dlls/wined3d/directx.c index 8c58a346add..667bcdde0c9 100644 --- a/dlls/wined3d/directx.c +++ b/dlls/wined3d/directx.c @@ -23,6 +23,7 @@
#include "wined3d_private.h" #include "winternl.h" +#include "wine/list.h"
WINE_DEFAULT_DEBUG_CHANNEL(d3d); WINE_DECLARE_DEBUG_CHANNEL(winediag); @@ -38,6 +39,19 @@ enum wined3d_driver_model DRIVER_MODEL_NT6X };
+struct wined3d_adapter_budget_change_notification +{ + const struct wined3d_adapter *adapter; + HANDLE event; + DWORD cookie; + UINT64 last_local_budget; + UINT64 last_non_local_budget; + struct list entry; +}; + +static struct list adapter_budget_change_notifications = LIST_INIT( adapter_budget_change_notifications ); +static HANDLE notification_thread, notification_thread_stop_event; + /* The d3d device ID */ static const GUID IID_D3DDEVICE_D3DUID = { 0xaeb2cdd4, 0x6e41, 0x43ea, { 0x94,0x1c,0x83,0x61,0xcc,0x76,0x07,0x81 } };
@@ -1046,6 +1060,135 @@ HRESULT CDECL wined3d_adapter_get_video_memory_info(const struct wined3d_adapter return WINED3D_OK; }
+static DWORD CALLBACK notification_thread_func(void *stop_event) +{ + struct wined3d_adapter_budget_change_notification *notification; + struct wined3d_video_memory_info info; + HRESULT hr; + + while (TRUE) + { + wined3d_mutex_lock(); + LIST_FOR_EACH_ENTRY(notification, &adapter_budget_change_notifications, + struct wined3d_adapter_budget_change_notification, entry) + { + hr = wined3d_adapter_get_video_memory_info(notification->adapter, 0, + WINED3D_MEMORY_SEGMENT_GROUP_LOCAL, &info); + if (SUCCEEDED(hr) && info.budget != notification->last_local_budget) + { + notification->last_local_budget = info.budget; + SetEvent(notification->event); + continue; + } + + hr = wined3d_adapter_get_video_memory_info(notification->adapter, 0, + WINED3D_MEMORY_SEGMENT_GROUP_NON_LOCAL, &info); + if (SUCCEEDED(hr) && info.budget != notification->last_non_local_budget) + { + notification->last_non_local_budget = info.budget; + SetEvent(notification->event); + } + } + wined3d_mutex_unlock(); + + if (WaitForSingleObject(stop_event, 1000) == WAIT_OBJECT_0) + break; + } + + return TRUE; +} + +HRESULT CDECL wined3d_adapter_register_budget_change_notification(const struct wined3d_adapter *adapter, + HANDLE event, DWORD *cookie) +{ + static DWORD cookie_counter; + static BOOL wrapped; + struct wined3d_adapter_budget_change_notification *notification, *new_notification; + HANDLE thread = NULL; + BOOL found = FALSE; + + new_notification = heap_alloc_zero(sizeof(*new_notification)); + if (!new_notification) + return E_OUTOFMEMORY; + + wined3d_mutex_lock(); + new_notification->adapter = adapter; + new_notification->event = event; + new_notification->cookie = cookie_counter++; + if (cookie_counter < new_notification->cookie) + wrapped = TRUE; + if (wrapped) + { + while (TRUE) + { + LIST_FOR_EACH_ENTRY(notification, &adapter_budget_change_notifications, + struct wined3d_adapter_budget_change_notification, entry) + { + if (notification->cookie == new_notification->cookie) + { + found = TRUE; + break; + } + } + + if (!found) + break; + + new_notification->cookie = cookie_counter++; + } + } + + *cookie = new_notification->cookie; + list_add_head(&adapter_budget_change_notifications, &new_notification->entry); + + if (!notification_thread) + { + notification_thread_stop_event = CreateEventW(0, FALSE, FALSE, NULL); + thread = CreateThread(NULL, 0, notification_thread_func, notification_thread_stop_event, + CREATE_SUSPENDED, NULL); + notification_thread = thread; + } + wined3d_mutex_unlock(); + if (thread) + ResumeThread(thread); + return WINED3D_OK; +} + +HRESULT CDECL wined3d_adapter_unregister_budget_change_notification(DWORD cookie) +{ + struct wined3d_adapter_budget_change_notification *notification; + HANDLE thread, thread_stop_event; + + wined3d_mutex_lock(); + LIST_FOR_EACH_ENTRY(notification, &adapter_budget_change_notifications, + struct wined3d_adapter_budget_change_notification, entry) + { + if (notification->cookie == cookie) + { + list_remove(¬ification->entry); + heap_free(notification); + break; + } + } + + if (!list_empty(&adapter_budget_change_notifications)) + { + wined3d_mutex_unlock(); + return WINED3D_OK; + } + + thread = notification_thread; + thread_stop_event = notification_thread_stop_event; + notification_thread = NULL; + notification_thread_stop_event = NULL; + wined3d_mutex_unlock(); + SetEvent(thread_stop_event); + WaitForSingleObject(thread, INFINITE); + CloseHandle(thread); + CloseHandle(thread_stop_event); + return WINED3D_OK; +} + HRESULT CDECL wined3d_register_software_device(struct wined3d *wined3d, void *init_function) { FIXME("wined3d %p, init_function %p stub!\n", wined3d, init_function); diff --git a/dlls/wined3d/wined3d.spec b/dlls/wined3d/wined3d.spec index 845ca0fb170..63220e1222c 100644 --- a/dlls/wined3d/wined3d.spec +++ b/dlls/wined3d/wined3d.spec @@ -22,6 +22,8 @@ @ cdecl wined3d_adapter_get_output(ptr long) @ cdecl wined3d_adapter_get_output_count(ptr) @ cdecl wined3d_adapter_get_video_memory_info(ptr long long ptr) +@ cdecl wined3d_adapter_register_budget_change_notification(ptr ptr ptr) +@ cdecl wined3d_adapter_unregister_budget_change_notification(long)
@ cdecl wined3d_blend_state_create(ptr ptr ptr ptr ptr) @ cdecl wined3d_blend_state_decref(ptr) diff --git a/include/wine/wined3d.h b/include/wine/wined3d.h index 2b7a626f751..116aea82348 100644 --- a/include/wine/wined3d.h +++ b/include/wine/wined3d.h @@ -2364,6 +2364,9 @@ unsigned int __cdecl wined3d_adapter_get_output_count(const struct wined3d_adapt HRESULT __cdecl wined3d_adapter_get_video_memory_info(const struct wined3d_adapter *adapter, unsigned int node_idx, enum wined3d_memory_segment_group group, struct wined3d_video_memory_info *info); +HRESULT __cdecl wined3d_adapter_register_budget_change_notification(const struct wined3d_adapter *adapter, + HANDLE event, DWORD *cookie); +HRESULT __cdecl wined3d_adapter_unregister_budget_change_notification(DWORD cookie);
HRESULT __cdecl wined3d_buffer_create(struct wined3d_device *device, const struct wined3d_buffer_desc *desc, const struct wined3d_sub_resource_data *data, void *parent, const struct wined3d_parent_ops *parent_ops,
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=114982
Your paranoid android.
=== w10pro64_en_AE_u8 (64 bit report) ===
dxgi: dxgi.c:2831: Test failed: Got unexpected hr 0x887a0022. dxgi.c:2834: Test failed: Got unexpected fullscreen 0. dxgi.c:2837: Test failed: Got unexpected hr 0x887a0022. dxgi.c:2840: Test failed: Got unexpected fullscreen 0.
=== w10pro64_zh_CN (64 bit report) ===
dxgi: dxgi.c:2831: Test failed: Got unexpected hr 0x887a0022. dxgi.c:2834: Test failed: Got unexpected fullscreen 0. dxgi.c:2837: Test failed: Got unexpected hr 0x887a0022. dxgi.c:2840: Test failed: Got unexpected fullscreen 0.
On 5/18/22 01:49, Zhiyi Zhang wrote:
@@ -1046,6 +1060,135 @@ HRESULT CDECL wined3d_adapter_get_video_memory_info(const struct wined3d_adapter return WINED3D_OK; }
+static DWORD CALLBACK notification_thread_func(void *stop_event) +{
- struct wined3d_adapter_budget_change_notification *notification;
- struct wined3d_video_memory_info info;
- HRESULT hr;
- while (TRUE)
- {
wined3d_mutex_lock();
LIST_FOR_EACH_ENTRY(notification, &adapter_budget_change_notifications,
struct wined3d_adapter_budget_change_notification, entry)
{
hr = wined3d_adapter_get_video_memory_info(notification->adapter, 0,
WINED3D_MEMORY_SEGMENT_GROUP_LOCAL, &info);
if (SUCCEEDED(hr) && info.budget != notification->last_local_budget)
{
notification->last_local_budget = info.budget;
SetEvent(notification->event);
continue;
}
hr = wined3d_adapter_get_video_memory_info(notification->adapter, 0,
WINED3D_MEMORY_SEGMENT_GROUP_NON_LOCAL, &info);
if (SUCCEEDED(hr) && info.budget != notification->last_non_local_budget)
{
notification->last_non_local_budget = info.budget;
SetEvent(notification->event);
}
}
wined3d_mutex_unlock();
if (WaitForSingleObject(stop_event, 1000) == WAIT_OBJECT_0)
break;
The test also waits for 1 second, which seems like it'd be liable to fail intermittently, if I read this correctly. Should we increase the polling interval here? (Or reduce it in the test?)
- }
- return TRUE;
+}
+HRESULT CDECL wined3d_adapter_register_budget_change_notification(const struct wined3d_adapter *adapter,
HANDLE event, DWORD *cookie)
+{
- static DWORD cookie_counter;
- static BOOL wrapped;
- struct wined3d_adapter_budget_change_notification *notification, *new_notification;
- HANDLE thread = NULL;
- BOOL found = FALSE;
- new_notification = heap_alloc_zero(sizeof(*new_notification));
- if (!new_notification)
return E_OUTOFMEMORY;
- wined3d_mutex_lock();
- new_notification->adapter = adapter;
- new_notification->event = event;
- new_notification->cookie = cookie_counter++;
- if (cookie_counter < new_notification->cookie)
wrapped = TRUE;
- if (wrapped)
- {
while (TRUE)
{
LIST_FOR_EACH_ENTRY(notification, &adapter_budget_change_notifications,
struct wined3d_adapter_budget_change_notification, entry)
{
if (notification->cookie == new_notification->cookie)
{
found = TRUE;
break;
}
}
if (!found)
break;
new_notification->cookie = cookie_counter++;
}
- }
- *cookie = new_notification->cookie;
- list_add_head(&adapter_budget_change_notifications, &new_notification->entry);
- if (!notification_thread)
- {
notification_thread_stop_event = CreateEventW(0, FALSE, FALSE, NULL);
thread = CreateThread(NULL, 0, notification_thread_func, notification_thread_stop_event,
CREATE_SUSPENDED, NULL);
notification_thread = thread;
- }
- wined3d_mutex_unlock();
- if (thread)
ResumeThread(thread);
What's the reason for creating the thread suspended?
- return WINED3D_OK;
+}
On 5/20/22 14:16, Zebediah Figura wrote:
On 5/18/22 01:49, Zhiyi Zhang wrote:
@@ -1046,6 +1060,135 @@ HRESULT CDECL wined3d_adapter_get_video_memory_info(const struct wined3d_adapter return WINED3D_OK; } +static DWORD CALLBACK notification_thread_func(void *stop_event) +{ + struct wined3d_adapter_budget_change_notification *notification; + struct wined3d_video_memory_info info; + HRESULT hr;
+ while (TRUE) + { + wined3d_mutex_lock(); + LIST_FOR_EACH_ENTRY(notification, &adapter_budget_change_notifications, + struct wined3d_adapter_budget_change_notification, entry) + { + hr = wined3d_adapter_get_video_memory_info(notification->adapter, 0, + WINED3D_MEMORY_SEGMENT_GROUP_LOCAL, &info); + if (SUCCEEDED(hr) && info.budget != notification->last_local_budget) + { + notification->last_local_budget = info.budget; + SetEvent(notification->event); + continue; + }
+ hr = wined3d_adapter_get_video_memory_info(notification->adapter, 0, + WINED3D_MEMORY_SEGMENT_GROUP_NON_LOCAL, &info); + if (SUCCEEDED(hr) && info.budget != notification->last_non_local_budget) + { + notification->last_non_local_budget = info.budget; + SetEvent(notification->event); + } + } + wined3d_mutex_unlock();
+ if (WaitForSingleObject(stop_event, 1000) == WAIT_OBJECT_0) + break;
The test also waits for 1 second, which seems like it'd be liable to fail intermittently, if I read this correctly. Should we increase the polling interval here? (Or reduce it in the test?)
I can probably reduce the timeout in the test. It was picked rather randomly and didn't mean the test would fail intermittently.
+ }
+ return TRUE; +}
+HRESULT CDECL wined3d_adapter_register_budget_change_notification(const struct wined3d_adapter *adapter, + HANDLE event, DWORD *cookie) +{ + static DWORD cookie_counter; + static BOOL wrapped; + struct wined3d_adapter_budget_change_notification *notification, *new_notification; + HANDLE thread = NULL; + BOOL found = FALSE;
+ new_notification = heap_alloc_zero(sizeof(*new_notification)); + if (!new_notification) + return E_OUTOFMEMORY;
+ wined3d_mutex_lock(); + new_notification->adapter = adapter; + new_notification->event = event; + new_notification->cookie = cookie_counter++; + if (cookie_counter < new_notification->cookie) + wrapped = TRUE; + if (wrapped) + { + while (TRUE) + { + LIST_FOR_EACH_ENTRY(notification, &adapter_budget_change_notifications, + struct wined3d_adapter_budget_change_notification, entry) + { + if (notification->cookie == new_notification->cookie) + { + found = TRUE; + break; + } + }
+ if (!found) + break;
+ new_notification->cookie = cookie_counter++; + } + }
+ *cookie = new_notification->cookie; + list_add_head(&adapter_budget_change_notifications, &new_notification->entry);
+ if (!notification_thread) + { + notification_thread_stop_event = CreateEventW(0, FALSE, FALSE, NULL); + thread = CreateThread(NULL, 0, notification_thread_func, notification_thread_stop_event, + CREATE_SUSPENDED, NULL); + notification_thread = thread; + } + wined3d_mutex_unlock(); + if (thread) + ResumeThread(thread);
What's the reason for creating the thread suspended?
Because the thread also calls wined3d_mutex_lock/unlock().
+ return WINED3D_OK; +}
On 5/20/22 15:05, Zhiyi Zhang wrote:
On 5/20/22 14:16, Zebediah Figura wrote:
On 5/18/22 01:49, Zhiyi Zhang wrote:
@@ -1046,6 +1060,135 @@ HRESULT CDECL wined3d_adapter_get_video_memory_info(const struct wined3d_adapter return WINED3D_OK; } +static DWORD CALLBACK notification_thread_func(void *stop_event) +{ + struct wined3d_adapter_budget_change_notification *notification; + struct wined3d_video_memory_info info; + HRESULT hr;
+ while (TRUE) + { + wined3d_mutex_lock(); + LIST_FOR_EACH_ENTRY(notification, &adapter_budget_change_notifications, + struct wined3d_adapter_budget_change_notification, entry) + { + hr = wined3d_adapter_get_video_memory_info(notification->adapter, 0, + WINED3D_MEMORY_SEGMENT_GROUP_LOCAL, &info); + if (SUCCEEDED(hr) && info.budget != notification->last_local_budget) + { + notification->last_local_budget = info.budget; + SetEvent(notification->event); + continue; + }
+ hr = wined3d_adapter_get_video_memory_info(notification->adapter, 0, + WINED3D_MEMORY_SEGMENT_GROUP_NON_LOCAL, &info); + if (SUCCEEDED(hr) && info.budget != notification->last_non_local_budget) + { + notification->last_non_local_budget = info.budget; + SetEvent(notification->event); + } + } + wined3d_mutex_unlock();
+ if (WaitForSingleObject(stop_event, 1000) == WAIT_OBJECT_0) + break;
The test also waits for 1 second, which seems like it'd be liable to fail intermittently, if I read this correctly. Should we increase the polling interval here? (Or reduce it in the test?)
I can probably reduce the timeout in the test. It was picked rather randomly and didn't mean the test would fail intermittently.
Are you worried that the event may not be fired within 1 second? If an adapter has any memory budget, the event will be immediately fired due to notification->last_local_budget and notification->last_non_local_budget being initialized to zero. The test's purpose is to test the event should be fired immediately, so the 1 second wait doesn't really matter.
Thanks, Zhiyi
+ }
+ return TRUE; +}
+HRESULT CDECL wined3d_adapter_register_budget_change_notification(const struct wined3d_adapter *adapter, + HANDLE event, DWORD *cookie) +{ + static DWORD cookie_counter; + static BOOL wrapped; + struct wined3d_adapter_budget_change_notification *notification, *new_notification; + HANDLE thread = NULL; + BOOL found = FALSE;
+ new_notification = heap_alloc_zero(sizeof(*new_notification)); + if (!new_notification) + return E_OUTOFMEMORY;
+ wined3d_mutex_lock(); + new_notification->adapter = adapter; + new_notification->event = event; + new_notification->cookie = cookie_counter++; + if (cookie_counter < new_notification->cookie) + wrapped = TRUE; + if (wrapped) + { + while (TRUE) + { + LIST_FOR_EACH_ENTRY(notification, &adapter_budget_change_notifications, + struct wined3d_adapter_budget_change_notification, entry) + { + if (notification->cookie == new_notification->cookie) + { + found = TRUE; + break; + } + }
+ if (!found) + break;
+ new_notification->cookie = cookie_counter++; + } + }
+ *cookie = new_notification->cookie; + list_add_head(&adapter_budget_change_notifications, &new_notification->entry);
+ if (!notification_thread) + { + notification_thread_stop_event = CreateEventW(0, FALSE, FALSE, NULL); + thread = CreateThread(NULL, 0, notification_thread_func, notification_thread_stop_event, + CREATE_SUSPENDED, NULL); + notification_thread = thread; + } + wined3d_mutex_unlock(); + if (thread) + ResumeThread(thread);
What's the reason for creating the thread suspended?
Because the thread also calls wined3d_mutex_lock/unlock().
+ return WINED3D_OK; +}
On 5/20/22 02:15, Zhiyi Zhang wrote:
On 5/20/22 15:05, Zhiyi Zhang wrote:
On 5/20/22 14:16, Zebediah Figura wrote:
On 5/18/22 01:49, Zhiyi Zhang wrote:
@@ -1046,6 +1060,135 @@ HRESULT CDECL wined3d_adapter_get_video_memory_info(const struct wined3d_adapter return WINED3D_OK; } +static DWORD CALLBACK notification_thread_func(void *stop_event) +{ + struct wined3d_adapter_budget_change_notification *notification; + struct wined3d_video_memory_info info; + HRESULT hr;
+ while (TRUE) + { + wined3d_mutex_lock(); + LIST_FOR_EACH_ENTRY(notification, &adapter_budget_change_notifications, + struct wined3d_adapter_budget_change_notification, entry) + { + hr = wined3d_adapter_get_video_memory_info(notification->adapter, 0, + WINED3D_MEMORY_SEGMENT_GROUP_LOCAL, &info); + if (SUCCEEDED(hr) && info.budget != notification->last_local_budget) + { + notification->last_local_budget = info.budget; + SetEvent(notification->event); + continue; + }
+ hr = wined3d_adapter_get_video_memory_info(notification->adapter, 0, + WINED3D_MEMORY_SEGMENT_GROUP_NON_LOCAL, &info); + if (SUCCEEDED(hr) && info.budget != notification->last_non_local_budget) + { + notification->last_non_local_budget = info.budget; + SetEvent(notification->event); + } + } + wined3d_mutex_unlock();
+ if (WaitForSingleObject(stop_event, 1000) == WAIT_OBJECT_0) + break;
The test also waits for 1 second, which seems like it'd be liable to fail intermittently, if I read this correctly. Should we increase the polling interval here? (Or reduce it in the test?)
I can probably reduce the timeout in the test. It was picked rather randomly and didn't mean the test would fail intermittently.
Are you worried that the event may not be fired within 1 second? If an adapter has any memory budget, the event will be immediately fired due to notification->last_local_budget and notification->last_non_local_budget being initialized to zero. The test's purpose is to test the event should be fired immediately, so the 1 second wait doesn't really matter.
Ah, indeed.
Although that raises another question—is every subscriber supposed to be notified immediately? And if so, that won't happen currently, not without alerting the thread that a new subscriber has been added. If the notification doesn't need to be immediate there's not a problem though...
Thanks, Zhiyi
+ }
+ return TRUE; +}
+HRESULT CDECL wined3d_adapter_register_budget_change_notification(const struct wined3d_adapter *adapter, + HANDLE event, DWORD *cookie) +{ + static DWORD cookie_counter; + static BOOL wrapped; + struct wined3d_adapter_budget_change_notification *notification, *new_notification; + HANDLE thread = NULL; + BOOL found = FALSE;
+ new_notification = heap_alloc_zero(sizeof(*new_notification)); + if (!new_notification) + return E_OUTOFMEMORY;
+ wined3d_mutex_lock(); + new_notification->adapter = adapter; + new_notification->event = event; + new_notification->cookie = cookie_counter++; + if (cookie_counter < new_notification->cookie) + wrapped = TRUE; + if (wrapped) + { + while (TRUE) + { + LIST_FOR_EACH_ENTRY(notification, &adapter_budget_change_notifications, + struct wined3d_adapter_budget_change_notification, entry) + { + if (notification->cookie == new_notification->cookie) + { + found = TRUE; + break; + } + }
+ if (!found) + break;
+ new_notification->cookie = cookie_counter++; + } + }
+ *cookie = new_notification->cookie; + list_add_head(&adapter_budget_change_notifications, &new_notification->entry);
+ if (!notification_thread) + { + notification_thread_stop_event = CreateEventW(0, FALSE, FALSE, NULL); + thread = CreateThread(NULL, 0, notification_thread_func, notification_thread_stop_event, + CREATE_SUSPENDED, NULL); + notification_thread = thread; + } + wined3d_mutex_unlock(); + if (thread) + ResumeThread(thread);
What's the reason for creating the thread suspended?
Because the thread also calls wined3d_mutex_lock/unlock().
Right, but that shouldn't require suspending it. It just means that if the thread starts executing, it'll wait on the wined3d mutex until we release it subsequently.
+ return WINED3D_OK; +}